diff --git a/Cargo.lock b/Cargo.lock index 5c951494..55efca06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,8 +108,8 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" dependencies = [ - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -223,8 +223,8 @@ checksum = "2262160a7ae29e3415554a3f1fc04c764b1540c116aa524683208078b7a75bc9" dependencies = [ "actix-router", "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -441,8 +441,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc" dependencies = [ "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -508,7 +508,7 @@ dependencies = [ "log", "peeking_take_while", "proc-macro2", - "quote 1.0.23", + "quote 1.0.32", "regex", "rustc-hash", "shlex", @@ -640,8 +640,8 @@ dependencies = [ "cached_proc_macro_types", "darling", "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -889,8 +889,8 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -915,9 +915,9 @@ dependencies = [ "codespan-reporting", "once_cell", "proc-macro2", - "quote 1.0.23", + "quote 1.0.32", "scratch", - "syn", + "syn 1.0.109", ] [[package]] @@ -933,8 +933,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56" dependencies = [ "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -956,9 +956,9 @@ dependencies = [ "fnv", "ident_case", "proc-macro2", - "quote 1.0.23", + "quote 1.0.32", "strsim 0.10.0", - "syn", + "syn 1.0.109", ] [[package]] @@ -968,8 +968,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -999,9 +999,9 @@ checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", "proc-macro2", - "quote 1.0.23", + "quote 1.0.32", "rustc_version", - "syn", + "syn 1.0.109", ] [[package]] @@ -1065,8 +1065,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "355f93763ef7b0ae1c43c4d8eccc9d5848d84ad1a1d8ce61c421d1ac85a19d05" dependencies = [ "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -1077,8 +1077,8 @@ checksum = "11f36e95862220b211a6e2aa5eca09b4fa391b13cd52ceb8035a24bf65a79de2" dependencies = [ "once_cell", "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -1094,6 +1094,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.2.8" @@ -1269,8 +1275,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -1347,8 +1353,8 @@ checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" dependencies = [ "proc-macro-error", "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -1441,8 +1447,8 @@ dependencies = [ "proc-macro-crate", "proc-macro-error", "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -1760,7 +1766,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.2", "slab", "tokio", "tokio-util", @@ -1788,6 +1794,12 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heck" version = "0.4.1" @@ -1987,7 +1999,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ "proc-macro2", - "quote 1.0.23", + "quote 1.0.32", ] [[package]] @@ -2000,6 +2012,16 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + [[package]] name = "instant" version = "0.1.12" @@ -2466,8 +2488,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -2538,8 +2560,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -2647,10 +2669,10 @@ dependencies = [ "mime", "proc-macro-error", "proc-macro2", - "quote 1.0.23", + "quote 1.0.32", "strum", "strum_macros", - "syn", + "syn 1.0.109", ] [[package]] @@ -2716,8 +2738,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -2781,9 +2803,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48dd52a5211fac27e7acb14cfc9f30ae16ae0e956b7b779c8214c74559cef4c3" dependencies = [ "proc-macro2", - "quote 1.0.23", + "quote 1.0.32", "regex", - "syn", + "syn 1.0.109", ] [[package]] @@ -2885,8 +2907,8 @@ checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", "version_check", ] @@ -2897,15 +2919,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", - "quote 1.0.23", + "quote 1.0.32", "version_check", ] [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -2928,9 +2950,9 @@ checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" [[package]] name = "quote" -version = "1.0.23" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -3190,29 +3212,29 @@ checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 2.0.28", ] [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -3250,7 +3272,7 @@ version = "0.9.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f82e6c8c047aa50a7328632d067bcae6ef38772a79e28daf32f735e0e4f3dd10" dependencies = [ - "indexmap", + "indexmap 1.9.2", "itoa", "ryu", "serde", @@ -3320,8 +3342,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b64f9e531ce97c88b4778aad0ceee079216071cffec6ac9b904277f8f92e7fe3" dependencies = [ "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -3431,9 +3453,9 @@ checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck", "proc-macro2", - "quote 1.0.23", + "quote 1.0.32", "rustversion", - "syn", + "syn 1.0.109", ] [[package]] @@ -3468,7 +3490,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote 1.0.23", + "quote 1.0.32", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +dependencies = [ + "proc-macro2", + "quote 1.0.32", "unicode-ident", ] @@ -3551,8 +3584,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" dependencies = [ "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -3665,8 +3698,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -3716,17 +3749,17 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] name = "toml_edit" -version = "0.19.4" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ - "indexmap", + "indexmap 2.0.0", "toml_datetime", "winnow", ] @@ -3779,8 +3812,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -3855,8 +3888,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "258bc1c4f8e2e73a977812ab339d503e6feeb92700f6d07a6de4d321522d5c08" dependencies = [ "lazy_static", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", ] [[package]] @@ -3914,8 +3947,8 @@ checksum = "9f807fdb3151fee75df7485b901a89624358cd07a67a8fb1a5831bf5a07681ff" dependencies = [ "Inflector", "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", "termcolor", ] @@ -4094,9 +4127,9 @@ dependencies = [ "lazy_static", "proc-macro-error", "proc-macro2", - "quote 1.0.23", + "quote 1.0.32", "regex", - "syn", + "syn 1.0.109", "validator_types", ] @@ -4107,7 +4140,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "111abfe30072511849c5910134e8baf8dc05de4c0e5903d681cbd5c9c4d611e3" dependencies = [ "proc-macro2", - "syn", + "syn 1.0.109", ] [[package]] @@ -4214,8 +4247,8 @@ dependencies = [ "log", "once_cell", "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -4237,7 +4270,7 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ - "quote 1.0.23", + "quote 1.0.32", "wasm-bindgen-macro-support", ] @@ -4248,8 +4281,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", - "quote 1.0.23", - "syn", + "quote 1.0.32", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4448,9 +4481,9 @@ checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winnow" -version = "0.3.5" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f" +checksum = "acaaa1190073b2b101e15083c38ee8ec891b5e05cbee516521e94ec008f61e64" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 9aee9599..434e001b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,8 +32,8 @@ actix-web = "4.1.0" actix-web-validator = "5.0.1" actix-service = "2.0.2" actix-extensible-rate-limit = "0.2.1" -serde = { version = "1.0.140", features = ["derive"] } -serde_json = "1.0.82" +serde = { version = "1.0.183", features = ["derive"] } +serde_json = "1.0.104" validator = { version = "0.16", features = ["derive"] } ## FINAL diff --git a/src/stream/pipeline/fake_pipeline.rs b/src/stream/pipeline/fake_pipeline.rs index 8928a654..b12b18f7 100644 --- a/src/stream/pipeline/fake_pipeline.rs +++ b/src/stream/pipeline/fake_pipeline.rs @@ -1,7 +1,7 @@ use crate::{ stream::types::CaptureConfiguration, video::{ - types::{VideoEncodeType, VideoSourceType}, + types::{VideoEncodeType, VideoSourceType, DEFAULT_RAW_FORMAT, KNOWN_RTP_RAW_FORMATS}, video_source_gst::VideoSourceGstType, }, video_stream::types::VideoAndStreamInformation, @@ -70,7 +70,7 @@ impl FakePipeline { format!(concat!( "videotestsrc pattern={pattern} is-live=true do-timestamp=true", " ! timeoverlay", - " ! video/x-raw,format=I420", + " ! video/x-raw,format={format}", " ! x264enc tune=zerolatency speed-preset=ultrafast bitrate=5000", " ! h264parse", " ! capsfilter name={filter_name} caps=video/x-h264,profile={profile},stream-format=avc,alignment=au,width={width},height={height},framerate={interval_denominator}/{interval_numerator}", @@ -78,6 +78,7 @@ impl FakePipeline { " ! tee name={sink_tee_name} allow-not-linked=true" ), pattern = pattern, + format = DEFAULT_RAW_FORMAT, profile = "constrained-baseline", width = configuration.width, height = configuration.height, @@ -87,20 +88,22 @@ impl FakePipeline { sink_tee_name = sink_tee_name, ) } - VideoEncodeType::Yuyv => { + VideoEncodeType::Raw(fourcc) => { + let mut fourcc = fourcc.clone(); + if !KNOWN_RTP_RAW_FORMATS.contains(&fourcc.as_str()) { + fourcc = DEFAULT_RAW_FORMAT.to_string(); + } + format!( concat!( - // Because application-rtp templates doesn't accept "YUY2", we - // need to transcode it. We are arbitrarily chosing the closest - // format available ("UYVY"). "videotestsrc pattern={pattern} is-live=true do-timestamp=true", " ! timeoverlay", - " ! video/x-raw,format=I420", - " ! capsfilter name={filter_name} caps=video/x-raw,format=I420,width={width},height={height},framerate={interval_denominator}/{interval_numerator}", + " ! capsfilter name={filter_name} caps=video/x-raw,format={format},width={width},height={height},framerate={interval_denominator}/{interval_numerator}", " ! rtpvrawpay pt=96", " ! tee name={sink_tee_name} allow-not-linked=true", ), pattern = pattern, + format = fourcc, width = configuration.width, height = configuration.height, interval_denominator = configuration.frame_interval.denominator, @@ -114,13 +117,14 @@ impl FakePipeline { concat!( "videotestsrc pattern={pattern} is-live=true do-timestamp=true", " ! timeoverlay", - " ! video/x-raw,format=I420", + " ! video/x-raw,format={format}", " ! jpegenc quality=85 idct-method=1", " ! capsfilter name={filter_name} caps=image/jpeg,width={width},height={height},framerate={interval_denominator}/{interval_numerator}", " ! rtpjpegpay pt=96", " ! tee name={sink_tee_name} allow-not-linked=true", ), pattern = pattern, + format = DEFAULT_RAW_FORMAT, width = configuration.width, height = configuration.height, interval_denominator = configuration.frame_interval.denominator, diff --git a/src/stream/pipeline/v4l_pipeline.rs b/src/stream/pipeline/v4l_pipeline.rs index 465279fe..6efb639c 100644 --- a/src/stream/pipeline/v4l_pipeline.rs +++ b/src/stream/pipeline/v4l_pipeline.rs @@ -1,6 +1,6 @@ use crate::{ stream::types::CaptureConfiguration, - video::types::{VideoEncodeType, VideoSourceType}, + video::types::{VideoEncodeType, VideoSourceType, DEFAULT_RAW_FORMAT, KNOWN_RTP_RAW_FORMATS}, video_stream::types::VideoAndStreamInformation, }; @@ -69,16 +69,22 @@ impl V4lPipeline { sink_tee_name = sink_tee_name, ) } - VideoEncodeType::Yuyv => { + VideoEncodeType::Raw(fourcc) => { + let mut fourcc = fourcc.clone(); + if !KNOWN_RTP_RAW_FORMATS.contains(&fourcc.as_str()) { + fourcc = DEFAULT_RAW_FORMAT.to_string(); + } + format!( concat!( "v4l2src device={device} do-timestamp=false", " ! videoconvert", - " ! capsfilter name={filter_name} caps=video/x-raw,format=I420,width={width},height={height},framerate={interval_denominator}/{interval_numerator}", + " ! capsfilter name={filter_name} caps=video/x-raw,format={format},width={width},height={height},framerate={interval_denominator}/{interval_numerator}", " ! rtpvrawpay pt=96", " ! tee name={sink_tee_name} allow-not-linked=true" ), device = device, + format = fourcc, width = width, height = height, interval_denominator = interval_denominator, diff --git a/src/stream/sink/image_sink.rs b/src/stream/sink/image_sink.rs index 3b9adacb..91d8d013 100644 --- a/src/stream/sink/image_sink.rs +++ b/src/stream/sink/image_sink.rs @@ -356,7 +356,7 @@ impl ImageSink { _transcoding_elements.push(parser); _transcoding_elements.push(decoder); } - VideoEncodeType::Yuyv => { + VideoEncodeType::Raw(_) => { let depayloader = gst::ElementFactory::make("rtpvrawdepay").build()?; _transcoding_elements.push(depayloader); } diff --git a/src/video/types.rs b/src/video/types.rs index f51ea015..f3df52ed 100644 --- a/src/video/types.rs +++ b/src/video/types.rs @@ -5,6 +5,11 @@ use super::video_source_redirect::VideoSourceRedirect; use paperclip::actix::Apiv2Schema; use serde::{Deserialize, Serialize}; +pub const KNOWN_RTP_RAW_FORMATS: [&str; 9] = [ + "RGB", "RGBA", "BGR", "BGRA", "AYUV", "UYVY", "I420", "Y41B", "UYVP", +]; +pub const DEFAULT_RAW_FORMAT: &str = "I420"; + #[derive(Apiv2Schema, Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum VideoSourceType { Gst(VideoSourceGst), @@ -12,14 +17,16 @@ pub enum VideoSourceType { Redirect(VideoSourceRedirect), } -#[derive(Apiv2Schema, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] +#[derive(Apiv2Schema, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)] #[serde(rename_all = "UPPERCASE")] pub enum VideoEncodeType { - Unknown(String), H265, H264, Mjpg, - Yuyv, + #[serde(untagged)] + Raw(String), + #[serde(untagged)] + Unknown(String), } #[derive(Apiv2Schema, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] @@ -102,15 +109,64 @@ impl VideoSourceType { } impl VideoEncodeType { - //TODO: use trait fromstr, check others places pub fn from_str(fourcc: &str) -> VideoEncodeType { let fourcc = fourcc.to_uppercase(); - match fourcc.as_str() { - "H264" => VideoEncodeType::H264, - "MJPG" => VideoEncodeType::Mjpg, - "YUYV" => VideoEncodeType::Yuyv, - _ => VideoEncodeType::Unknown(fourcc), + + let fourcc = match fourcc.as_str() { + "H264" => return VideoEncodeType::H264, + "MJPG" => return VideoEncodeType::Mjpg, + // TODO: We can implement a [GstDeviceMonitor](https://gstreamer.freedesktop.org/documentation/gstreamer/gstdevicemonitor.html?gi-language=c) and then this manual mapping between v4l's and gst's fourcc will not be neccessary anymore. + // A list of possible v4l fourcc from the Linux docs: + // - [YUV](https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/yuv-formats.html) + // - [RGB](https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-rgb.html) + // - [compressed](https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-013.html) + "YUYV" => "YUY2".to_string(), + _ => fourcc, + }; + + // Use Gstreamer to recognize raw formats. [GStreamer raw formats](https://gstreamer.freedesktop.org/documentation/additional/design/mediatype-video-raw.html?gi-language=c#formats) + use std::ffi::CString; + if let Ok(c_char_array) = CString::new(fourcc.clone()).map(|c_str| c_str.into_raw()) { + use gst_video::ffi::*; + + let gst_video_format = unsafe { gst_video_format_from_string(c_char_array) }; + + if !matches!( + gst_video_format, + GST_VIDEO_FORMAT_UNKNOWN | GST_VIDEO_FORMAT_ENCODED + ) { + return VideoEncodeType::Raw(fourcc); + } + } + + VideoEncodeType::Unknown(fourcc) + } +} +impl<'de> Deserialize<'de> for VideoEncodeType { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct VideoEncodeTypeVisitor; + + use std::fmt; + + impl<'de> serde::de::Visitor<'de> for VideoEncodeTypeVisitor { + type Value = VideoEncodeType; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("valid video encode type") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + Ok(Self::Value::from_str(value)) + } } + + deserializer.deserialize_str(VideoEncodeTypeVisitor) } } @@ -141,3 +197,27 @@ pub static STANDARD_SIZES: &[(u32, u32); 16] = &[ (320, 240), (256, 144), ]; + +mod tests { + + #[test] + fn test_video_encode_type_serde() { + use super::VideoEncodeType; + + for (encode, encode_str) in [ + (VideoEncodeType::Mjpg, "\"MJPG\""), + (VideoEncodeType::H264, "\"H264\""), + (VideoEncodeType::Raw("I420".to_string()), "\"I420\""), + (VideoEncodeType::Unknown("POTATO".to_string()), "\"POTATO\""), + ] { + debug_assert_eq!(encode_str, serde_json::to_string(&encode).unwrap()); + + debug_assert_eq!(encode, serde_json::from_str(encode_str).unwrap()); + } + + // Ensure UYUV is parsed as YUY2: + let encode = VideoEncodeType::Raw("YUY2".to_string()); + let legacy_encode_str = "\"YUYV\""; + debug_assert_eq!(encode, serde_json::from_str(legacy_encode_str).unwrap()); + } +} diff --git a/src/video/video_source_gst.rs b/src/video/video_source_gst.rs index 63823db2..f589376f 100644 --- a/src/video/video_source_gst.rs +++ b/src/video/video_source_gst.rs @@ -66,7 +66,7 @@ impl VideoSource for VideoSourceGst { sizes: sizes.clone(), }, Format { - encode: VideoEncodeType::Yuyv, + encode: VideoEncodeType::Raw(DEFAULT_RAW_FORMAT.to_string()), sizes: sizes.clone(), }, Format { diff --git a/src/video/video_source_local.rs b/src/video/video_source_local.rs index 1aea91bc..ace97adc 100644 --- a/src/video/video_source_local.rs +++ b/src/video/video_source_local.rs @@ -777,11 +777,26 @@ mod device_identification_tests { let candidates = vec![ add_available_camera("A", "/dev/video0", "usb_port_0", vec![H264]), - add_available_camera("A", "/dev/video1", "usb_port_0", vec![Yuyv, Mjpg]), + add_available_camera( + "A", + "/dev/video1", + "usb_port_0", + vec![Raw(DEFAULT_RAW_FORMAT.to_string()), Mjpg], + ), add_available_camera("A", "/dev/video2", "usb_port_1", vec![H264]), - add_available_camera("A", "/dev/video3", "usb_port_1", vec![Yuyv, Mjpg]), + add_available_camera( + "A", + "/dev/video3", + "usb_port_1", + vec![Raw(DEFAULT_RAW_FORMAT.to_string()), Mjpg], + ), add_available_camera("B", "/dev/video4", "usb_port_2", vec![H264]), - add_available_camera("B", "/dev/video5", "usb_port_2", vec![Yuyv, Mjpg]), + add_available_camera( + "B", + "/dev/video5", + "usb_port_2", + vec![Raw(DEFAULT_RAW_FORMAT.to_string()), Mjpg], + ), ]; let same_name_candidates = VideoSourceLocal::get_cameras_with_same_name(&candidates, "A"); @@ -798,8 +813,18 @@ mod device_identification_tests { let candidates = vec![ add_available_camera("A", "/dev/video0", "usb_port_0", vec![H264]), add_available_camera("B", "/dev/video1", "usb_port_1", vec![H264]), - add_available_camera("C", "/dev/video2", "usb_port_0", vec![Yuyv, Mjpg]), - add_available_camera("D", "/dev/video3", "usb_port_1", vec![Yuyv, Mjpg]), + add_available_camera( + "C", + "/dev/video2", + "usb_port_0", + vec![Raw(DEFAULT_RAW_FORMAT.to_string()), Mjpg], + ), + add_available_camera( + "D", + "/dev/video3", + "usb_port_1", + vec![Raw(DEFAULT_RAW_FORMAT.to_string()), Mjpg], + ), ]; let same_encode_candidates = @@ -816,9 +841,19 @@ mod device_identification_tests { let candidates = vec![ add_available_camera("A", "/dev/video0", "usb_port_0", vec![H264]), - add_available_camera("B", "/dev/video1", "usb_port_0", vec![Yuyv, Mjpg]), + add_available_camera( + "B", + "/dev/video1", + "usb_port_0", + vec![Raw(DEFAULT_RAW_FORMAT.to_string()), Mjpg], + ), add_available_camera("C", "/dev/video2", "usb_port_1", vec![H264]), - add_available_camera("D", "/dev/video3", "usb_port_1", vec![Yuyv, Mjpg]), + add_available_camera( + "D", + "/dev/video3", + "usb_port_1", + vec![Raw(DEFAULT_RAW_FORMAT.to_string()), Mjpg], + ), ]; let same_encode_candidates = VideoSourceLocal::get_cameras_with_same_bus( @@ -838,10 +873,25 @@ mod device_identification_tests { let candidates = vec![ add_available_camera("A", "/dev/video0", "usb_port_0", vec![H264]), - add_available_camera("A", "/dev/video1", "usb_port_0", vec![Yuyv, Mjpg]), + add_available_camera( + "A", + "/dev/video1", + "usb_port_0", + vec![Raw(DEFAULT_RAW_FORMAT.to_string()), Mjpg], + ), add_available_camera("B", "/dev/video2", "usb_port_1", vec![H264]), - add_available_camera("B", "/dev/video3", "usb_port_1", vec![Yuyv, Mjpg]), - add_available_camera("C", "/dev/video3", "usb_port_1", vec![H264, Yuyv, Mjpg]), + add_available_camera( + "B", + "/dev/video3", + "usb_port_1", + vec![Raw(DEFAULT_RAW_FORMAT.to_string()), Mjpg], + ), + add_available_camera( + "C", + "/dev/video3", + "usb_port_1", + vec![H264, Raw(DEFAULT_RAW_FORMAT.to_string()), Mjpg], + ), ]; let stream = create_stream("A", "/dev/video0", "usb_port_0", H264); let (VideoSourceType::Local(source), CaptureConfiguration::Video(capture_configuration)) = (&stream.video_source, &stream.stream_information.configuration) else { @@ -878,9 +928,19 @@ mod device_identification_tests { let candidates = vec![ add_available_camera("A", "/dev/video0", current_usb_bus, vec![H264]), - add_available_camera("A", "/dev/video1", current_usb_bus, vec![Yuyv, Mjpg]), + add_available_camera( + "A", + "/dev/video1", + current_usb_bus, + vec![Raw(DEFAULT_RAW_FORMAT.to_string()), Mjpg], + ), add_available_camera("B", "/dev/video2", "usb_port_3", vec![H264]), - add_available_camera("B", "/dev/video3", "usb_port_3", vec![Yuyv, Mjpg]), + add_available_camera( + "B", + "/dev/video3", + "usb_port_3", + vec![Raw(DEFAULT_RAW_FORMAT.to_string()), Mjpg], + ), ]; for n in (0..3).collect::>() { @@ -922,8 +982,18 @@ mod device_identification_tests { let candidates = vec![ add_available_camera("A", current_path, "usb_port_0", vec![H264]), add_available_camera("A", last_path, "usb_port_1", vec![H264]), - add_available_camera("A", "/dev/video3", "usb_port_0", vec![Yuyv, Mjpg]), - add_available_camera("A", "/dev/video5", "usb_port_1", vec![Yuyv, Mjpg]), + add_available_camera( + "A", + "/dev/video3", + "usb_port_0", + vec![Raw(DEFAULT_RAW_FORMAT.to_string()), Mjpg], + ), + add_available_camera( + "A", + "/dev/video5", + "usb_port_1", + vec![Raw(DEFAULT_RAW_FORMAT.to_string()), Mjpg], + ), ]; for n in (0..=1).collect::>() { @@ -957,9 +1027,19 @@ mod device_identification_tests { let candidates = vec![ add_available_camera("A", "/dev/video0", current_usb_bus, vec![H264]), - add_available_camera("A", "/dev/video1", current_usb_bus, vec![Yuyv, Mjpg]), + add_available_camera( + "A", + "/dev/video1", + current_usb_bus, + vec![Raw(DEFAULT_RAW_FORMAT.to_string()), Mjpg], + ), add_available_camera("A", "/dev/video4", "usb_port_2", vec![H264]), - add_available_camera("A", "/dev/video5", "usb_port_2", vec![Yuyv, Mjpg]), + add_available_camera( + "A", + "/dev/video5", + "usb_port_2", + vec![Raw(DEFAULT_RAW_FORMAT.to_string()), Mjpg], + ), ]; for n in (0..5).collect::>() {