diff --git a/Cargo.lock b/Cargo.lock index c77ae4e6..7654e6cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -352,7 +352,7 @@ dependencies = [ "hex", "http 0.2.12", "hyper 0.14.28", - "ring", + "ring 0.17.8", "time", "tokio", "tracing", @@ -611,7 +611,7 @@ dependencies = [ "once_cell", "p256", "percent-encoding", - "ring", + "ring 0.17.8", "sha2", "subtle", "time", @@ -1744,7 +1744,7 @@ dependencies = [ "futures-core", "futures-sink", "nanorand", - "spin", + "spin 0.9.8", ] [[package]] @@ -2208,11 +2208,11 @@ dependencies = [ "byte-slice-cast", "ebur128", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-app", "gstreamer-audio", - "gstreamer-base", - "gstreamer-check", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "hrtf", "nnnoiseless", "num-traits", @@ -2239,10 +2239,10 @@ dependencies = [ "futures", "gio", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-audio", - "gstreamer-base", - "gstreamer-check", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "once_cell", "percent-encoding", "rand", @@ -2261,9 +2261,9 @@ dependencies = [ "cdg", "cdg_renderer", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-app", - "gstreamer-base", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video", "image", "once_cell", @@ -2277,9 +2277,9 @@ dependencies = [ "byte-slice-cast", "claxon", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-audio", - "gstreamer-check", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "once_cell", ] @@ -2296,9 +2296,9 @@ dependencies = [ "chrono", "either", "gst-plugin-version-helper", - "gstreamer", - "gstreamer-base", - "gstreamer-check", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video", "once_cell", "pango", @@ -2319,10 +2319,10 @@ dependencies = [ "byte-slice-cast", "csound", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-audio", - "gstreamer-base", - "gstreamer-check", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "once_cell", ] @@ -2332,8 +2332,8 @@ version = "0.13.0-alpha.1" dependencies = [ "dav1d", "gst-plugin-version-helper", - "gstreamer", - "gstreamer-base", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video", "num_cpus", "once_cell", @@ -2346,11 +2346,11 @@ dependencies = [ "gio", "gst-plugin-gtk4", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-app", "gstreamer-audio", - "gstreamer-base", - "gstreamer-check", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video", "gtk4", "once_cell", @@ -2364,8 +2364,8 @@ dependencies = [ "byte-slice-cast", "ffv1", "gst-plugin-version-helper", - "gstreamer", - "gstreamer-check", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video", "once_cell", ] @@ -2375,8 +2375,8 @@ name = "gst-plugin-file" version = "0.13.0-alpha.1" dependencies = [ "gst-plugin-version-helper", - "gstreamer", - "gstreamer-base", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "once_cell", "url", ] @@ -2388,9 +2388,9 @@ dependencies = [ "byteorder", "flavors", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-audio", - "gstreamer-base", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "nom", "num-rational", "once_cell", @@ -2405,11 +2405,11 @@ dependencies = [ "chrono", "dash-mpd", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-app", "gstreamer-audio", - "gstreamer-base", - "gstreamer-check", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-pbutils", "gstreamer-video", "m3u8-rs", @@ -2425,8 +2425,8 @@ dependencies = [ "atomic_refcell", "gif", "gst-plugin-version-helper", - "gstreamer", - "gstreamer-check", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video", "once_cell", ] @@ -2437,9 +2437,9 @@ version = "0.13.0-alpha.1" dependencies = [ "anyhow", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-app", - "gstreamer-check", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video", "once_cell", ] @@ -2453,9 +2453,9 @@ dependencies = [ "gdk4-win32", "gdk4-x11", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-allocators", - "gstreamer-base", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-gl", "gstreamer-gl-egl", "gstreamer-gl-wayland", @@ -2474,10 +2474,10 @@ dependencies = [ "chrono", "gio", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-app", "gstreamer-audio", - "gstreamer-check", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-pbutils", "gstreamer-video", "m3u8-rs", @@ -2491,10 +2491,10 @@ version = "0.13.0-alpha.1" dependencies = [ "byte-slice-cast", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-audio", - "gstreamer-base", - "gstreamer-check", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video", "num-traits", "once_cell", @@ -2507,9 +2507,9 @@ dependencies = [ "anyhow", "futures", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-app", - "gstreamer-check", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-utils", "once_cell", "pretty_assertions", @@ -2523,8 +2523,8 @@ name = "gst-plugin-json" version = "0.13.0-alpha.1" dependencies = [ "gst-plugin-version-helper", - "gstreamer", - "gstreamer-check", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "once_cell", "serde", "serde_json", @@ -2537,9 +2537,9 @@ dependencies = [ "atomic_refcell", "byte-slice-cast", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-audio", - "gstreamer-check", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "lewton", "once_cell", ] @@ -2551,9 +2551,9 @@ dependencies = [ "gio", "gst-plugin-gtk4", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-audio", - "gstreamer-check", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gtk4", "num-rational", "once_cell", @@ -2566,9 +2566,9 @@ version = "0.13.0-alpha.1" dependencies = [ "anyhow", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-audio", - "gstreamer-base", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-pbutils", "gstreamer-video", "once_cell", @@ -2587,9 +2587,9 @@ dependencies = [ "data-encoding", "glib", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-audio", - "gstreamer-base", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video", "libloading", "once_cell", @@ -2605,8 +2605,8 @@ dependencies = [ "cairo-rs", "chrono", "gst-plugin-version-helper", - "gstreamer", - "gstreamer-base", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-rtp", "gstreamer-video", "once_cell", @@ -2623,7 +2623,7 @@ dependencies = [ "atomic_refcell", "glib", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video", "once_cell", ] @@ -2633,22 +2633,42 @@ name = "gst-plugin-png" version = "0.13.0-alpha.1" dependencies = [ "gst-plugin-version-helper", - "gstreamer", - "gstreamer-check", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video", "once_cell", "parking_lot", "png", ] +[[package]] +name = "gst-plugin-quinn" +version = "0.13.0-alpha.1" +dependencies = [ + "bytes", + "futures", + "gst-plugin-version-helper", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs)", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs)", + "once_cell", + "quinn", + "rcgen", + "rustls", + "rustls-pemfile 1.0.4", + "serial_test", + "thiserror", + "tokio", +] + [[package]] name = "gst-plugin-raptorq" version = "0.13.0-alpha.1" dependencies = [ "gst-plugin-version-helper", - "gstreamer", - "gstreamer-base", - "gstreamer-check", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-rtp", "once_cell", "rand", @@ -2661,8 +2681,8 @@ version = "0.13.0-alpha.1" dependencies = [ "atomic_refcell", "gst-plugin-version-helper", - "gstreamer", - "gstreamer-check", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video", "once_cell", "rav1e", @@ -2673,8 +2693,8 @@ name = "gst-plugin-regex" version = "0.13.0-alpha.1" dependencies = [ "gst-plugin-version-helper", - "gstreamer", - "gstreamer-check", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "once_cell", "regex", ] @@ -2686,8 +2706,8 @@ dependencies = [ "bytes", "futures", "gst-plugin-version-helper", - "gstreamer", - "gstreamer-base", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "headers 0.4.0", "http-body-util", "hyper 1.3.1", @@ -2709,10 +2729,10 @@ dependencies = [ "byte-slice-cast", "chrono", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-app", "gstreamer-audio", - "gstreamer-check", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-rtp", "gstreamer-video", "hex", @@ -2734,7 +2754,7 @@ dependencies = [ "data-encoding", "futures", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-app", "gstreamer-net", "gstreamer-pbutils", @@ -2755,10 +2775,10 @@ version = "0.13.0-alpha.1" dependencies = [ "clap", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-app", - "gstreamer-base", - "gstreamer-check", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "hex", "once_cell", "pretty_assertions", @@ -2776,8 +2796,8 @@ dependencies = [ "anyhow", "futures", "gst-plugin-version-helper", - "gstreamer", - "gstreamer-base", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "librespot", "once_cell", "tokio", @@ -2789,7 +2809,7 @@ name = "gst-plugin-textahead" version = "0.13.0-alpha.1" dependencies = [ "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "once_cell", ] @@ -2798,8 +2818,8 @@ name = "gst-plugin-textwrap" version = "0.13.0-alpha.1" dependencies = [ "gst-plugin-version-helper", - "gstreamer", - "gstreamer-check", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "hyphenation", "once_cell", "textwrap", @@ -2818,10 +2838,10 @@ dependencies = [ "futures", "gio", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-app", "gstreamer-audio", - "gstreamer-check", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-net", "gstreamer-rtp", "once_cell", @@ -2844,9 +2864,9 @@ dependencies = [ "gio", "gst-plugin-gtk4", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-audio", - "gstreamer-check", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video", "gtk4", "once_cell", @@ -2859,7 +2879,7 @@ version = "0.13.0-alpha.1" dependencies = [ "anyhow", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "once_cell", "regex", "signal-hook", @@ -2871,9 +2891,9 @@ version = "0.13.0-alpha.1" dependencies = [ "byte-slice-cast", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-audio", - "gstreamer-base", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video", "num-traits", "once_cell", @@ -2886,7 +2906,7 @@ dependencies = [ "anyhow", "clap", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-app", "more-asserts", "once_cell", @@ -2912,9 +2932,9 @@ dependencies = [ "color-thief", "dssim-core", "gst-plugin-version-helper", - "gstreamer", - "gstreamer-base", - "gstreamer-check", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video", "image", "image_hasher", @@ -2927,8 +2947,8 @@ name = "gst-plugin-webp" version = "0.13.0-alpha.1" dependencies = [ "gst-plugin-version-helper", - "gstreamer", - "gstreamer-check", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video", "libwebp-sys2", "once_cell", @@ -2959,10 +2979,10 @@ dependencies = [ "gst-plugin-rtp", "gst-plugin-version-helper", "gst-plugin-webrtc-signalling-protocol", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-app", "gstreamer-audio", - "gstreamer-base", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-net", "gstreamer-rtp", "gstreamer-sdp", @@ -3031,7 +3051,7 @@ dependencies = [ "bytes", "futures", "gst-plugin-version-helper", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-sdp", "gstreamer-webrtc", "once_cell", @@ -3050,7 +3070,7 @@ dependencies = [ "futures-core", "futures-util", "glib", - "gstreamer-sys", + "gstreamer-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "itertools 0.12.1", "libc", "muldiv", @@ -3066,13 +3086,37 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gstreamer" +version = "0.23.0" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#b7b5352353e147fccc00ff684dffb65c21a6e6b1" +dependencies = [ + "cfg-if", + "futures-channel", + "futures-core", + "futures-util", + "glib", + "gstreamer-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs)", + "itertools 0.12.1", + "libc", + "muldiv", + "num-integer", + "num-rational", + "once_cell", + "option-operations", + "paste", + "pin-project-lite", + "smallvec", + "thiserror", +] + [[package]] name = "gstreamer-allocators" version = "0.23.0" source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" dependencies = [ "glib", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-allocators-sys", "libc", "once_cell", @@ -3085,7 +3129,7 @@ source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main# dependencies = [ "glib-sys", "gobject-sys", - "gstreamer-sys", + "gstreamer-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "libc", "system-deps", ] @@ -3098,9 +3142,9 @@ dependencies = [ "futures-core", "futures-sink", "glib", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-app-sys", - "gstreamer-base", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "libc", ] @@ -3110,8 +3154,8 @@ version = "0.23.0" source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" dependencies = [ "glib-sys", - "gstreamer-base-sys", - "gstreamer-sys", + "gstreamer-base-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "libc", "system-deps", ] @@ -3123,9 +3167,9 @@ source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main# dependencies = [ "cfg-if", "glib", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-audio-sys", - "gstreamer-base", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "libc", "once_cell", "serde", @@ -3139,8 +3183,8 @@ source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main# dependencies = [ "glib-sys", "gobject-sys", - "gstreamer-base-sys", - "gstreamer-sys", + "gstreamer-base-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "libc", "system-deps", ] @@ -3153,8 +3197,21 @@ dependencies = [ "atomic_refcell", "cfg-if", "glib", - "gstreamer", - "gstreamer-base-sys", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-base-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "libc", +] + +[[package]] +name = "gstreamer-base" +version = "0.23.0" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#b7b5352353e147fccc00ff684dffb65c21a6e6b1" +dependencies = [ + "atomic_refcell", + "cfg-if", + "glib", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs)", + "gstreamer-base-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs)", "libc", ] @@ -3165,7 +3222,19 @@ source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main# dependencies = [ "glib-sys", "gobject-sys", - "gstreamer-sys", + "gstreamer-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "libc", + "system-deps", +] + +[[package]] +name = "gstreamer-base-sys" +version = "0.23.0" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#b7b5352353e147fccc00ff684dffb65c21a6e6b1" +dependencies = [ + "glib-sys", + "gobject-sys", + "gstreamer-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs)", "libc", "system-deps", ] @@ -3176,8 +3245,18 @@ version = "0.23.0" source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" dependencies = [ "glib", - "gstreamer", - "gstreamer-check-sys", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-check-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", +] + +[[package]] +name = "gstreamer-check" +version = "0.23.0" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#b7b5352353e147fccc00ff684dffb65c21a6e6b1" +dependencies = [ + "glib", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs)", + "gstreamer-check-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs)", ] [[package]] @@ -3187,7 +3266,19 @@ source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main# dependencies = [ "glib-sys", "gobject-sys", - "gstreamer-sys", + "gstreamer-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "libc", + "system-deps", +] + +[[package]] +name = "gstreamer-check-sys" +version = "0.23.0" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#b7b5352353e147fccc00ff684dffb65c21a6e6b1" +dependencies = [ + "glib-sys", + "gobject-sys", + "gstreamer-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs)", "libc", "system-deps", ] @@ -3198,8 +3289,8 @@ version = "0.23.0" source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" dependencies = [ "glib", - "gstreamer", - "gstreamer-base", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-gl-sys", "gstreamer-video", "libc", @@ -3212,7 +3303,7 @@ version = "0.23.0" source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" dependencies = [ "glib", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-gl", "gstreamer-gl-egl-sys", "libc", @@ -3236,8 +3327,8 @@ source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main# dependencies = [ "glib-sys", "gobject-sys", - "gstreamer-base-sys", - "gstreamer-sys", + "gstreamer-base-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video-sys", "libc", "system-deps", @@ -3249,7 +3340,7 @@ version = "0.23.0" source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" dependencies = [ "glib", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-gl", "gstreamer-gl-wayland-sys", "libc", @@ -3272,7 +3363,7 @@ version = "0.23.0" source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" dependencies = [ "glib", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-gl", "gstreamer-gl-x11-sys", "libc", @@ -3296,7 +3387,7 @@ source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main# dependencies = [ "gio", "glib", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-net-sys", ] @@ -3307,7 +3398,7 @@ source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main# dependencies = [ "gio-sys", "glib-sys", - "gstreamer-sys", + "gstreamer-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "libc", "system-deps", ] @@ -3318,7 +3409,7 @@ version = "0.23.0" source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" dependencies = [ "glib", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-audio", "gstreamer-pbutils-sys", "gstreamer-video", @@ -3334,7 +3425,7 @@ dependencies = [ "glib-sys", "gobject-sys", "gstreamer-audio-sys", - "gstreamer-sys", + "gstreamer-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video-sys", "libc", "system-deps", @@ -3346,7 +3437,7 @@ version = "0.23.0" source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" dependencies = [ "glib", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-rtp-sys", "libc", ] @@ -3357,8 +3448,8 @@ version = "0.23.0" source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" dependencies = [ "glib-sys", - "gstreamer-base-sys", - "gstreamer-sys", + "gstreamer-base-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "libc", "system-deps", ] @@ -3369,7 +3460,7 @@ version = "0.23.0" source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" dependencies = [ "glib", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-sdp-sys", ] @@ -3379,7 +3470,7 @@ version = "0.23.0" source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" dependencies = [ "glib-sys", - "gstreamer-sys", + "gstreamer-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "libc", "system-deps", ] @@ -3395,12 +3486,23 @@ dependencies = [ "system-deps", ] +[[package]] +name = "gstreamer-sys" +version = "0.23.0" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs#b7b5352353e147fccc00ff684dffb65c21a6e6b1" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + [[package]] name = "gstreamer-utils" version = "0.23.0" source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" dependencies = [ - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-app", "gstreamer-video", "once_cell", @@ -3415,8 +3517,8 @@ dependencies = [ "cfg-if", "futures-channel", "glib", - "gstreamer", - "gstreamer-base", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-base 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-video-sys", "libc", "once_cell", @@ -3431,8 +3533,8 @@ source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main# dependencies = [ "glib-sys", "gobject-sys", - "gstreamer-base-sys", - "gstreamer-sys", + "gstreamer-base-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", + "gstreamer-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "libc", "system-deps", ] @@ -3443,7 +3545,7 @@ version = "0.23.0" source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" dependencies = [ "glib", - "gstreamer", + "gstreamer 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "gstreamer-sdp", "gstreamer-webrtc-sys", "libc", @@ -3456,7 +3558,7 @@ source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main# dependencies = [ "glib-sys", "gstreamer-sdp-sys", - "gstreamer-sys", + "gstreamer-sys 0.23.0 (git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main)", "libc", "system-deps", ] @@ -3783,7 +3885,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2 0.5.6", "tokio", "tower-service", "tracing", @@ -4113,7 +4215,7 @@ checksum = "5c7ea04a7c5c055c175f189b6dc6ba036fd62306b58c66c9f6389036c503a3f4" dependencies = [ "base64 0.21.7", "js-sys", - "ring", + "ring 0.17.8", "serde", "serde_json", ] @@ -4606,7 +4708,7 @@ dependencies = [ "log", "memchr", "mime", - "spin", + "spin 0.9.8", "version_check", ] @@ -5045,6 +5147,16 @@ dependencies = [ "hmac 0.11.0", ] +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.0", + "serde", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -5272,7 +5384,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" dependencies = [ "bytes", - "heck 0.4.1", + "heck 0.5.0", "itertools 0.12.1", "log", "multimap 0.10.0", @@ -5359,6 +5471,54 @@ dependencies = [ "serde", ] +[[package]] +name = "quinn" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a" +dependencies = [ + "bytes", + "rand", + "ring 0.16.20", + "rustc-hash", + "rustls", + "rustls-native-certs", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" +dependencies = [ + "bytes", + "libc", + "socket2 0.5.6", + "tracing", + "windows-sys 0.48.0", +] + [[package]] name = "quote" version = "1.0.36" @@ -5470,6 +5630,18 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rcgen" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48406db8ac1f3cbc7dcdb56ec355343817958a356ff430259bb07baf7607e1e1" +dependencies = [ + "pem", + "ring 0.17.8", + "time", + "yasna", +] + [[package]] name = "realfft" version = "3.3.0" @@ -5644,6 +5816,21 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + [[package]] name = "ring" version = "0.17.8" @@ -5654,8 +5841,8 @@ dependencies = [ "cfg-if", "getrandom", "libc", - "spin", - "untrusted", + "spin 0.9.8", + "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -5711,6 +5898,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.4.0" @@ -5764,7 +5957,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" dependencies = [ "log", - "ring", + "ring 0.17.8", "rustls-webpki", "sct", ] @@ -5812,8 +6005,8 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -5867,8 +6060,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -6212,6 +6405,12 @@ dependencies = [ "serde", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spin" version = "0.9.8" @@ -6821,6 +7020,12 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -7329,6 +7534,15 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + [[package]] name = "zerocopy" version = "0.6.6" diff --git a/Cargo.toml b/Cargo.toml index 9d803a18..0af8a443 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ members = [ "net/webrtc", "net/webrtc/protocol", "net/webrtc/signalling", + "net/quinn", "text/ahead", "text/json", diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json index 3c57c068..6526cc80 100644 --- a/docs/plugins/gst_plugins_cache.json +++ b/docs/plugins/gst_plugins_cache.json @@ -3889,6 +3889,324 @@ "tracers": {}, "url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs" }, + "quinn": { + "description": "GStreamer Plugin for QUIC", + "elements": { + "quinnquicsink": { + "author": "Sanchayan Maity ", + "description": "Send data over the network via QUIC", + "hierarchy": [ + "GstQuinnQUICSink", + "GstBaseSink", + "GstElement", + "GstObject", + "GInitiallyUnowned", + "GObject" + ], + "klass": "Source/Network/QUIC", + "pad-templates": { + "sink": { + "caps": "ANY", + "direction": "sink", + "presence": "always" + } + }, + "properties": { + "alpn-protocols": { + "blurb": "QUIC connection Application-Layer Protocol Negotiation (ALPN) values", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "mutable": "null", + "readable": true, + "type": "GstValueArray", + "writable": true + }, + "certificate-file": { + "blurb": "Path to certificate chain in single file", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "NULL", + "mutable": "null", + "readable": true, + "type": "gchararray", + "writable": true + }, + "client-address": { + "blurb": "Address to be used by this QUIC client e.g. 127.0.0.1", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "127.0.0.1", + "mutable": "null", + "readable": true, + "type": "gchararray", + "writable": true + }, + "client-port": { + "blurb": "Port to be used by this QUIC client e.g. 5001", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "5001", + "max": "65535", + "min": "0", + "mutable": "null", + "readable": true, + "type": "guint", + "writable": true + }, + "private-key-file": { + "blurb": "Path to a PKCS8 or RSA private key file", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "NULL", + "mutable": "null", + "readable": true, + "type": "gchararray", + "writable": true + }, + "secure-connection": { + "blurb": "Use certificates for QUIC connection. False: Insecure connection, True: Secure connection.", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "true", + "mutable": "null", + "readable": true, + "type": "gboolean", + "writable": true + }, + "server-address": { + "blurb": "Address of the QUIC server to connect to e.g. 127.0.0.1", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "127.0.0.1", + "mutable": "null", + "readable": true, + "type": "gchararray", + "writable": true + }, + "server-name": { + "blurb": "Name of the QUIC server which is in server certificate", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "localhost", + "mutable": "null", + "readable": true, + "type": "gchararray", + "writable": true + }, + "server-port": { + "blurb": "Port of the QUIC server to connect to e.g. 5000", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "5000", + "max": "65535", + "min": "0", + "mutable": "null", + "readable": true, + "type": "guint", + "writable": true + }, + "timeout": { + "blurb": "Value in seconds to timeout QUIC endpoint requests (0 = No timeout).", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "15", + "max": "3600", + "min": "0", + "mutable": "null", + "readable": true, + "type": "guint", + "writable": true + }, + "use-datagram": { + "blurb": "Use datagram for lower latency, unreliable messaging", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "false", + "mutable": "null", + "readable": true, + "type": "gboolean", + "writable": true + } + }, + "rank": "marginal" + }, + "quinnquicsrc": { + "author": "Sanchayan Maity ", + "description": "Receive data over the network via QUIC", + "hierarchy": [ + "GstQuinnQUICSrc", + "GstBaseSrc", + "GstElement", + "GstObject", + "GInitiallyUnowned", + "GObject" + ], + "klass": "Source/Network/QUIC", + "pad-templates": { + "src": { + "caps": "ANY", + "direction": "src", + "presence": "always" + } + }, + "properties": { + "alpn-protocols": { + "blurb": "QUIC connection Application-Layer Protocol Negotiation (ALPN) values", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "mutable": "null", + "readable": true, + "type": "GstValueArray", + "writable": true + }, + "caps": { + "blurb": "The caps of the source pad", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "ANY", + "mutable": "null", + "readable": true, + "type": "GstCaps", + "writable": true + }, + "certificate-file": { + "blurb": "Path to certificate chain in single file", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "NULL", + "mutable": "null", + "readable": true, + "type": "gchararray", + "writable": true + }, + "private-key-file": { + "blurb": "Path to a PKCS8 or RSA private key file", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "NULL", + "mutable": "null", + "readable": true, + "type": "gchararray", + "writable": true + }, + "secure-connection": { + "blurb": "Use certificates for QUIC connection. False: Insecure connection, True: Secure connection.", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "true", + "mutable": "null", + "readable": true, + "type": "gboolean", + "writable": true + }, + "server-address": { + "blurb": "Address of the QUIC server e.g. 127.0.0.1", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "127.0.0.1", + "mutable": "null", + "readable": true, + "type": "gchararray", + "writable": true + }, + "server-name": { + "blurb": "Name of the QUIC server which is in server certificate", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "localhost", + "mutable": "null", + "readable": true, + "type": "gchararray", + "writable": true + }, + "server-port": { + "blurb": "Port of the QUIC server e.g. 5000", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "5000", + "max": "65535", + "min": "0", + "mutable": "null", + "readable": true, + "type": "guint", + "writable": true + }, + "timeout": { + "blurb": "Value in seconds to timeout QUIC endpoint requests (0 = No timeout).", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "15", + "max": "3600", + "min": "0", + "mutable": "null", + "readable": true, + "type": "guint", + "writable": true + }, + "use-datagram": { + "blurb": "Use datagram for lower latency, unreliable messaging", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "false", + "mutable": "null", + "readable": true, + "type": "gboolean", + "writable": true + } + }, + "rank": "marginal" + } + }, + "filename": "gstquinn", + "license": "MPL", + "other-types": {}, + "package": "gst-plugin-quinn", + "source": "gst-plugin-quinn", + "tracers": {}, + "url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs" + }, "raptorq": { "description": "GStreamer RaptorQ FEC Plugin", "elements": { diff --git a/meson.build b/meson.build index a069e709..8cf5dadb 100644 --- a/meson.build +++ b/meson.build @@ -205,6 +205,7 @@ plugins = { 'extra-deps': {'cairo-gobject': []}, }, 'gopbuffer': {'library': 'libgstgopbuffer'}, + 'quinn': {'library': 'libgstquinn'}, } if get_option('examples').allowed() diff --git a/meson_options.txt b/meson_options.txt index 7d416fa6..b9305abd 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -34,6 +34,7 @@ option('rtsp', type: 'feature', value: 'auto', description: 'Build rtsp plugin') option('rtp', type: 'feature', value: 'auto', description: 'Build rtp plugin') option('webrtc', type: 'feature', value: 'auto', yield: true, description: 'Build webrtc plugin') option('webrtchttp', type: 'feature', value: 'auto', description: 'Build webrtchttp plugin') +option('quinn', type: 'feature', value: 'auto', description: 'Build quinn plugin') # text option('textahead', type: 'feature', value: 'auto', description: 'Build textahead plugin') diff --git a/net/quinn/Cargo.toml b/net/quinn/Cargo.toml new file mode 100644 index 00000000..a092b954 --- /dev/null +++ b/net/quinn/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "gst-plugin-quinn" +version.workspace = true +authors = ["Sanchayan Maity +// +// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0. +// If a copy of the MPL was not distributed with this file, You can obtain one at +// . +// +// SPDX-License-Identifier: MPL-2.0 +#![allow(clippy::non_send_fields_in_send_ty, unused_doc_comments)] + +/** + * plugin-quinn: + * + * Since: plugins-rs-0.13.0 + */ +use gst::glib; +mod quinnquicsink; +mod quinnquicsrc; +mod utils; + +fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { + quinnquicsink::register(plugin)?; + quinnquicsrc::register(plugin)?; + + Ok(()) +} + +gst::plugin_define!( + quinn, + env!("CARGO_PKG_DESCRIPTION"), + plugin_init, + concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMIT_ID")), + "MPL", + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_REPOSITORY"), + env!("BUILD_REL_DATE") +); diff --git a/net/quinn/src/quinnquicsink/imp.rs b/net/quinn/src/quinnquicsink/imp.rs new file mode 100644 index 00000000..888facbd --- /dev/null +++ b/net/quinn/src/quinnquicsink/imp.rs @@ -0,0 +1,571 @@ +// Copyright (C) 2024, Asymptotic Inc. +// Author: Sanchayan Maity +// +// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0. +// If a copy of the MPL was not distributed with this file, You can obtain one at +// . +// +// SPDX-License-Identifier: MPL-2.0 + +use crate::utils::{ + client_endpoint, make_socket_addr, wait, WaitError, CONNECTION_CLOSE_CODE, CONNECTION_CLOSE_MSG, +}; +use bytes::Bytes; +use futures::future; +use gst::{glib, prelude::*, subclass::prelude::*}; +use gst_base::subclass::prelude::*; +use once_cell::sync::Lazy; +use quinn::{Connection, SendStream}; +use std::path::PathBuf; +use std::sync::Mutex; + +static DEFAULT_SERVER_NAME: &str = "localhost"; +static DEFAULT_SERVER_ADDR: &str = "127.0.0.1"; +static DEFAULT_SERVER_PORT: u16 = 5000; +static DEFAULT_CLIENT_ADDR: &str = "127.0.0.1"; +static DEFAULT_CLIENT_PORT: u16 = 5001; + +/* + * For QUIC transport parameters + * + * + * A HTTP client might specify "http/1.1" and/or "h2" or "h3". + * Other well-known values are listed in the at IANA registry at + * . + */ +const DEFAULT_ALPN: &str = "gst-quinn"; +const DEFAULT_TIMEOUT: u32 = 15; +const DEFAULT_SECURE_CONNECTION: bool = true; + +static CAT: Lazy = Lazy::new(|| { + gst::DebugCategory::new( + "quinnquicsink", + gst::DebugColorFlags::empty(), + Some("Quinn QUIC Sink"), + ) +}); + +struct Started { + connection: Connection, + stream: Option, +} + +#[derive(Default)] +enum State { + #[default] + Stopped, + Started(Started), +} + +#[derive(Clone, Debug)] +struct Settings { + client_address: String, + client_port: u16, + server_address: String, + server_port: u16, + server_name: String, + alpns: Vec, + timeout: u32, + secure_conn: bool, + use_datagram: bool, + certificate_file: Option, + private_key_file: Option, +} + +impl Default for Settings { + fn default() -> Self { + Settings { + client_address: DEFAULT_CLIENT_ADDR.to_string(), + client_port: DEFAULT_CLIENT_PORT, + server_address: DEFAULT_SERVER_ADDR.to_string(), + server_port: DEFAULT_SERVER_PORT, + server_name: DEFAULT_SERVER_NAME.to_string(), + alpns: vec![DEFAULT_ALPN.to_string()], + timeout: DEFAULT_TIMEOUT, + secure_conn: DEFAULT_SECURE_CONNECTION, + use_datagram: false, + certificate_file: None, + private_key_file: None, + } + } +} + +pub struct QuinnQuicSink { + settings: Mutex, + state: Mutex, + canceller: Mutex>, +} + +impl Default for QuinnQuicSink { + fn default() -> Self { + Self { + settings: Mutex::new(Settings::default()), + state: Mutex::new(State::default()), + canceller: Mutex::new(None), + } + } +} + +impl GstObjectImpl for QuinnQuicSink {} + +impl ElementImpl for QuinnQuicSink { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Quinn QUIC Sink", + "Source/Network/QUIC", + "Send data over the network via QUIC", + "Sanchayan Maity ", + ) + }); + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy> = Lazy::new(|| { + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &gst::Caps::new_any(), + ) + .unwrap(); + + vec![sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + + fn change_state( + &self, + transition: gst::StateChange, + ) -> Result { + if transition == gst::StateChange::NullToReady { + let settings = self.settings.lock().unwrap(); + + /* + * Fail the state change if a secure connection was requested but + * no certificate path was provided. + */ + if settings.secure_conn + && (settings.certificate_file.is_none() || settings.private_key_file.is_none()) + { + gst::error!( + CAT, + imp: self, + "Certificate or private key file not provided for secure connection" + ); + return Err(gst::StateChangeError); + } + } + + self.parent_change_state(transition) + } +} + +impl ObjectImpl for QuinnQuicSink { + fn constructed(&self) { + self.parent_constructed(); + } + + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy> = Lazy::new(|| { + vec![ + glib::ParamSpecString::builder("server-name") + .nick("QUIC server name") + .blurb("Name of the QUIC server which is in server certificate") + .build(), + glib::ParamSpecString::builder("server-address") + .nick("QUIC server address") + .blurb("Address of the QUIC server to connect to e.g. 127.0.0.1") + .build(), + glib::ParamSpecUInt::builder("server-port") + .nick("QUIC server port") + .blurb("Port of the QUIC server to connect to e.g. 5000") + .maximum(65535) + .default_value(DEFAULT_SERVER_PORT as u32) + .readwrite() + .build(), + glib::ParamSpecString::builder("client-address") + .nick("QUIC client address") + .blurb("Address to be used by this QUIC client e.g. 127.0.0.1") + .build(), + glib::ParamSpecUInt::builder("client-port") + .nick("QUIC client port") + .blurb("Port to be used by this QUIC client e.g. 5001") + .maximum(65535) + .default_value(DEFAULT_CLIENT_PORT as u32) + .readwrite() + .build(), + gst::ParamSpecArray::builder("alpn-protocols") + .nick("QUIC ALPN values") + .blurb("QUIC connection Application-Layer Protocol Negotiation (ALPN) values") + .element_spec(&glib::ParamSpecString::builder("alpn-protocol").build()) + .build(), + glib::ParamSpecUInt::builder("timeout") + .nick("Timeout") + .blurb("Value in seconds to timeout QUIC endpoint requests (0 = No timeout).") + .maximum(3600) + .default_value(DEFAULT_TIMEOUT) + .readwrite() + .build(), + glib::ParamSpecBoolean::builder("secure-connection") + .nick("Use secure connection") + .blurb("Use certificates for QUIC connection. False: Insecure connection, True: Secure connection.") + .default_value(DEFAULT_SECURE_CONNECTION) + .build(), + glib::ParamSpecString::builder("certificate-file") + .nick("Certificate file") + .blurb("Path to certificate chain in single file") + .build(), + glib::ParamSpecString::builder("private-key-file") + .nick("Private key file") + .blurb("Path to a PKCS8 or RSA private key file") + .build(), + glib::ParamSpecBoolean::builder("use-datagram") + .nick("Use datagram") + .blurb("Use datagram for lower latency, unreliable messaging") + .default_value(false) + .build(), + ] + }); + + PROPERTIES.as_ref() + } + + fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) { + let mut settings = self.settings.lock().unwrap(); + + match pspec.name() { + "server-name" => { + settings.server_name = value.get::().expect("type checked upstream"); + } + "server-address" => { + settings.server_address = value.get::().expect("type checked upstream"); + } + "server-port" => { + settings.server_port = value.get::().expect("type checked upstream") as u16; + } + "client-address" => { + settings.client_address = value.get::().expect("type checked upstream"); + } + "client-port" => { + settings.client_port = value.get::().expect("type checked upstream") as u16; + } + "alpn-protocols" => { + settings.alpns = value + .get::() + .expect("type checked upstream") + .as_slice() + .iter() + .map(|alpn| { + alpn.get::<&str>() + .expect("type checked upstream") + .to_string() + }) + .collect::>(); + } + "timeout" => { + settings.timeout = value.get().expect("type checked upstream"); + } + "secure-connection" => { + settings.secure_conn = value.get().expect("type checked upstream"); + } + "certificate-file" => { + let value: String = value.get().unwrap(); + settings.certificate_file = Some(value.into()); + } + "private-key-file" => { + let value: String = value.get().unwrap(); + settings.private_key_file = Some(value.into()); + } + "use-datagram" => { + settings.use_datagram = value.get().expect("type checked upstream"); + } + _ => unimplemented!(), + } + } + + fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + let settings = self.settings.lock().unwrap(); + + match pspec.name() { + "server-name" => settings.server_name.to_value(), + "server-address" => settings.server_address.to_string().to_value(), + "server-port" => { + let port = settings.server_port as u32; + port.to_value() + } + "client-address" => settings.client_address.to_string().to_value(), + "client-port" => { + let port = settings.client_port as u32; + port.to_value() + } + "alpn-protocols" => { + let alpns = settings.alpns.iter().map(|v| v.as_str()); + gst::Array::new(alpns).to_value() + } + "timeout" => settings.timeout.to_value(), + "secure-connection" => settings.secure_conn.to_value(), + "certificate-file" => { + let certfile = settings.certificate_file.as_ref(); + certfile.and_then(|file| file.to_str()).to_value() + } + "private-key-file" => { + let privkey = settings.private_key_file.as_ref(); + privkey.and_then(|file| file.to_str()).to_value() + } + "use-datagram" => settings.use_datagram.to_value(), + _ => unimplemented!(), + } + } +} + +#[glib::object_subclass] +impl ObjectSubclass for QuinnQuicSink { + const NAME: &'static str = "GstQuinnQUICSink"; + type Type = super::QuinnQuicSink; + type ParentType = gst_base::BaseSink; +} + +impl BaseSinkImpl for QuinnQuicSink { + fn start(&self) -> Result<(), gst::ErrorMessage> { + let settings = self.settings.lock().unwrap(); + let timeout = settings.timeout; + drop(settings); + + let mut state = self.state.lock().unwrap(); + + if let State::Started { .. } = *state { + unreachable!("QuicSink is already started"); + } + + match wait(&self.canceller, self.establish_connection(), timeout) { + Ok(Ok((c, s))) => { + *state = State::Started(Started { + connection: c, + stream: s, + }); + + gst::info!(CAT, imp: self, "Started"); + + Ok(()) + } + Ok(Err(e)) => match e { + WaitError::FutureAborted => { + gst::warning!(CAT, imp: self, "Connection aborted"); + Ok(()) + } + WaitError::FutureError(err) => { + gst::error!(CAT, imp: self, "Connection request failed: {}", err); + Err(gst::error_msg!( + gst::ResourceError::Failed, + ["Connection request failed: {}", err] + )) + } + }, + Err(e) => { + gst::error!(CAT, imp: self, "Failed to establish a connection: {:?}", e); + Err(gst::error_msg!( + gst::ResourceError::Failed, + ["Failed to establish a connection: {:?}", e] + )) + } + } + } + + fn stop(&self) -> Result<(), gst::ErrorMessage> { + let settings = self.settings.lock().unwrap(); + let timeout = settings.timeout; + let use_datagram = settings.use_datagram; + drop(settings); + + let mut state = self.state.lock().unwrap(); + + if let State::Started(ref mut state) = *state { + let connection = &state.connection; + let mut close_msg = CONNECTION_CLOSE_MSG.to_string(); + + if !use_datagram { + let send = &mut state.stream.as_mut().unwrap(); + + // Shutdown stream gracefully + match wait(&self.canceller, send.finish(), timeout) { + Ok(r) => { + if let Err(e) = r { + close_msg = format!("Stream finish request error: {}", e); + gst::error!(CAT, imp: self, "{}", close_msg); + } + } + Err(e) => match e { + WaitError::FutureAborted => { + close_msg = "Stream finish request aborted".to_string(); + gst::warning!(CAT, imp: self, "{}", close_msg); + } + WaitError::FutureError(e) => { + close_msg = format!("Stream finish request future error: {}", e); + gst::error!(CAT, imp: self, "{}", close_msg); + } + }, + }; + } + + connection.close(CONNECTION_CLOSE_CODE.into(), close_msg.as_bytes()); + } + + *state = State::Stopped; + + gst::info!(CAT, imp: self, "Stopped"); + + Ok(()) + } + + fn render(&self, buffer: &gst::Buffer) -> Result { + if let State::Stopped = *self.state.lock().unwrap() { + gst::element_imp_error!(self, gst::CoreError::Failed, ["Not started yet"]); + return Err(gst::FlowError::Error); + } + + gst::trace!(CAT, imp: self, "Rendering {:?}", buffer); + + let map = buffer.map_readable().map_err(|_| { + gst::element_imp_error!(self, gst::CoreError::Failed, ["Failed to map buffer"]); + gst::FlowError::Error + })?; + + match self.send_buffer(&map) { + Ok(_) => Ok(gst::FlowSuccess::Ok), + Err(err) => match err { + Some(error_message) => { + gst::error!(CAT, imp: self, "Data sending failed: {}", error_message); + self.post_error_message(error_message); + Err(gst::FlowError::Error) + } + _ => { + gst::info!(CAT, imp: self, "Send interrupted. Flushing..."); + Err(gst::FlowError::Flushing) + } + }, + } + } +} + +impl QuinnQuicSink { + fn send_buffer(&self, src: &[u8]) -> Result<(), Option> { + let settings = self.settings.lock().unwrap(); + let timeout = settings.timeout; + let use_datagram = settings.use_datagram; + drop(settings); + + let mut state = self.state.lock().unwrap(); + + let (conn, stream) = match *state { + State::Started(Started { + ref connection, + ref mut stream, + }) => (connection, stream), + State::Stopped => { + return Err(Some(gst::error_msg!( + gst::LibraryError::Failed, + ["Cannot send before start()"] + ))); + } + }; + + if use_datagram { + match conn.send_datagram(Bytes::copy_from_slice(src)) { + Ok(_) => Ok(()), + Err(e) => Err(Some(gst::error_msg!( + gst::ResourceError::Failed, + ["Sending data failed: {}", e] + ))), + } + } else { + let send = &mut stream.as_mut().unwrap(); + + match wait(&self.canceller, send.write_all(src), timeout) { + Ok(Ok(_)) => Ok(()), + Ok(Err(e)) => Err(Some(gst::error_msg!( + gst::ResourceError::Failed, + ["Sending data failed: {}", e] + ))), + Err(e) => match e { + WaitError::FutureAborted => { + gst::warning!(CAT, imp: self, "Sending aborted"); + Ok(()) + } + WaitError::FutureError(e) => Err(Some(gst::error_msg!( + gst::ResourceError::Failed, + ["Sending data failed: {}", e] + ))), + }, + } + } + } + + async fn establish_connection(&self) -> Result<(Connection, Option), WaitError> { + let client_addr; + let server_addr; + let server_name; + let alpns; + let use_datagram; + let secure_conn; + let cert_file; + let private_key_file; + + { + let settings = self.settings.lock().unwrap(); + + client_addr = make_socket_addr( + format!("{}:{}", settings.client_address, settings.client_port).as_str(), + )?; + server_addr = make_socket_addr( + format!("{}:{}", settings.server_address, settings.server_port).as_str(), + )?; + + server_name = settings.server_name.clone(); + alpns = settings.alpns.clone(); + use_datagram = settings.use_datagram; + secure_conn = settings.secure_conn; + cert_file = settings.certificate_file.clone(); + private_key_file = settings.private_key_file.clone(); + } + + let endpoint = + client_endpoint(client_addr, secure_conn, alpns, cert_file, private_key_file).map_err( + |err| { + WaitError::FutureError(gst::error_msg!( + gst::ResourceError::Failed, + ["Failed to configure endpoint: {}", err] + )) + }, + )?; + + let connection = endpoint + .connect(server_addr, &server_name) + .unwrap() + .await + .map_err(|err| { + WaitError::FutureError(gst::error_msg!( + gst::ResourceError::Failed, + ["Connection error: {}", err] + )) + })?; + + let stream = if !use_datagram { + let res = connection.open_uni().await.map_err(|err| { + WaitError::FutureError(gst::error_msg!( + gst::ResourceError::Failed, + ["Failed to open stream: {}", err] + )) + })?; + + Some(res) + } else { + None + }; + + Ok((connection, stream)) + } +} diff --git a/net/quinn/src/quinnquicsink/mod.rs b/net/quinn/src/quinnquicsink/mod.rs new file mode 100644 index 00000000..198ddd94 --- /dev/null +++ b/net/quinn/src/quinnquicsink/mod.rs @@ -0,0 +1,26 @@ +// Copyright (C) 2024, Asymptotic Inc. +// Author: Sanchayan Maity +//G +// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0. +// If a copy of the MPL was not distributed with this file, You can obtain one at +// . +// +// SPDX-License-Identifier: MPL-2.0 + +use gst::glib; +use gst::prelude::*; + +pub mod imp; + +glib::wrapper! { + pub struct QuinnQuicSink(ObjectSubclass) @extends gst_base::BaseSink, gst::Element, gst::Object; +} + +pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { + gst::Element::register( + Some(plugin), + "quinnquicsink", + gst::Rank::MARGINAL, + QuinnQuicSink::static_type(), + ) +} diff --git a/net/quinn/src/quinnquicsrc/imp.rs b/net/quinn/src/quinnquicsrc/imp.rs new file mode 100644 index 00000000..23f36190 --- /dev/null +++ b/net/quinn/src/quinnquicsrc/imp.rs @@ -0,0 +1,603 @@ +// Copyright (C) 2024, Asymptotic Inc. +// Author: Sanchayan Maity +// +// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0. +// If a copy of the MPL was not distributed with this file, You can obtain one at +// . +// +// SPDX-License-Identifier: MPL-2.0 + +use crate::utils::{ + make_socket_addr, server_endpoint, wait, WaitError, CONNECTION_CLOSE_CODE, CONNECTION_CLOSE_MSG, +}; +use bytes::Bytes; +use futures::future; +use gst::{glib, prelude::*, subclass::prelude::*}; +use gst_base::prelude::*; +use gst_base::subclass::base_src::CreateSuccess; +use gst_base::subclass::prelude::*; +use once_cell::sync::Lazy; +use quinn::{Connection, ConnectionError, RecvStream}; +use std::path::PathBuf; +use std::sync::Mutex; + +static DEFAULT_SERVER_NAME: &str = "localhost"; +static DEFAULT_SERVER_ADDR: &str = "127.0.0.1"; +static DEFAULT_SERVER_PORT: u16 = 5000; + +/* + * For QUIC transport parameters + * + * + * A HTTP client might specify "http/1.1" and/or "h2" or "h3". + * Other well-known values are listed in the at IANA registry at + * . + */ +const DEFAULT_ALPN: &str = "gst-quinn"; +const DEFAULT_TIMEOUT: u32 = 15; +const DEFAULT_SECURE_CONNECTION: bool = true; + +static CAT: Lazy = Lazy::new(|| { + gst::DebugCategory::new( + "quinnquicsrc", + gst::DebugColorFlags::empty(), + Some("Quinn QUIC Source"), + ) +}); + +struct Started { + connection: Connection, + stream: Option, +} + +#[derive(Default)] +enum State { + #[default] + Stopped, + Started(Started), +} + +#[derive(Clone, Debug)] +struct Settings { + server_address: String, + server_port: u16, + server_name: String, + alpns: Vec, + timeout: u32, + secure_conn: bool, + caps: gst::Caps, + use_datagram: bool, + certificate_file: Option, + private_key_file: Option, +} + +impl Default for Settings { + fn default() -> Self { + Settings { + server_address: DEFAULT_SERVER_ADDR.to_string(), + server_port: DEFAULT_SERVER_PORT, + server_name: DEFAULT_SERVER_NAME.to_string(), + alpns: vec![DEFAULT_ALPN.to_string()], + timeout: DEFAULT_TIMEOUT, + secure_conn: DEFAULT_SECURE_CONNECTION, + caps: gst::Caps::new_any(), + use_datagram: false, + certificate_file: None, + private_key_file: None, + } + } +} + +pub struct QuinnQuicSrc { + settings: Mutex, + state: Mutex, + canceller: Mutex>, +} + +impl Default for QuinnQuicSrc { + fn default() -> Self { + Self { + settings: Mutex::new(Settings::default()), + state: Mutex::new(State::default()), + canceller: Mutex::new(None), + } + } +} + +impl GstObjectImpl for QuinnQuicSrc {} + +impl ElementImpl for QuinnQuicSrc { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Quinn QUIC Source", + "Source/Network/QUIC", + "Receive data over the network via QUIC", + "Sanchayan Maity ", + ) + }); + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy> = Lazy::new(|| { + let sink_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &gst::Caps::new_any(), + ) + .unwrap(); + + vec![sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + + fn change_state( + &self, + transition: gst::StateChange, + ) -> Result { + if transition == gst::StateChange::NullToReady { + let settings = self.settings.lock().unwrap(); + + /* + * Fail the state change if a secure connection was requested but + * no certificate path was provided. + */ + if settings.secure_conn + && (settings.certificate_file.is_none() || settings.private_key_file.is_none()) + { + gst::error!( + CAT, + imp: self, + "Certificate or private key file not provided for secure connection" + ); + return Err(gst::StateChangeError); + } + } + + self.parent_change_state(transition) + } +} + +impl ObjectImpl for QuinnQuicSrc { + fn constructed(&self) { + self.parent_constructed(); + self.obj().set_format(gst::Format::Bytes); + } + + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy> = Lazy::new(|| { + vec![ + glib::ParamSpecString::builder("server-name") + .nick("QUIC server name") + .blurb("Name of the QUIC server which is in server certificate") + .build(), + glib::ParamSpecString::builder("server-address") + .nick("QUIC server address") + .blurb("Address of the QUIC server e.g. 127.0.0.1") + .build(), + glib::ParamSpecUInt::builder("server-port") + .nick("QUIC server port") + .blurb("Port of the QUIC server e.g. 5000") + .maximum(65535) + .default_value(DEFAULT_SERVER_PORT as u32) + .readwrite() + .build(), + gst::ParamSpecArray::builder("alpn-protocols") + .nick("QUIC ALPN values") + .blurb("QUIC connection Application-Layer Protocol Negotiation (ALPN) values") + .element_spec(&glib::ParamSpecString::builder("alpn-protocol").build()) + .build(), + glib::ParamSpecUInt::builder("timeout") + .nick("Timeout") + .blurb("Value in seconds to timeout QUIC endpoint requests (0 = No timeout).") + .maximum(3600) + .default_value(DEFAULT_TIMEOUT) + .readwrite() + .build(), + glib::ParamSpecBoolean::builder("secure-connection") + .nick("Use secure connection") + .blurb("Use certificates for QUIC connection. False: Insecure connection, True: Secure connection.") + .default_value(DEFAULT_SECURE_CONNECTION) + .build(), + glib::ParamSpecString::builder("certificate-file") + .nick("Certificate file") + .blurb("Path to certificate chain in single file") + .build(), + glib::ParamSpecString::builder("private-key-file") + .nick("Private key file") + .blurb("Path to a PKCS8 or RSA private key file") + .build(), + glib::ParamSpecBoxed::builder::("caps") + .nick("caps") + .blurb("The caps of the source pad") + .build(), + glib::ParamSpecBoolean::builder("use-datagram") + .nick("Use datagram") + .blurb("Use datagram for lower latency, unreliable messaging") + .default_value(false) + .build(), + ] + }); + + PROPERTIES.as_ref() + } + + fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) { + let mut settings = self.settings.lock().unwrap(); + + match pspec.name() { + "server-name" => { + settings.server_name = value.get::().expect("type checked upstream"); + } + "server-address" => { + settings.server_address = value.get::().expect("type checked upstream"); + } + "server-port" => { + settings.server_port = value.get::().expect("type checked upstream") as u16; + } + "alpn-protocols" => { + settings.alpns = value + .get::() + .expect("type checked upstream") + .as_slice() + .iter() + .map(|alpn| { + alpn.get::<&str>() + .expect("type checked upstream") + .to_string() + }) + .collect::>() + } + "caps" => { + settings.caps = value + .get::>() + .expect("type checked upstream") + .unwrap_or_else(gst::Caps::new_any); + + let srcpad = self.obj().static_pad("src").expect("source pad expected"); + srcpad.mark_reconfigure(); + } + "timeout" => { + settings.timeout = value.get().expect("type checked upstream"); + } + "secure-connection" => { + settings.secure_conn = value.get().expect("type checked upstream"); + } + "certificate-file" => { + let value: String = value.get().unwrap(); + settings.certificate_file = Some(value.into()); + } + "private-key-file" => { + let value: String = value.get().unwrap(); + settings.private_key_file = Some(value.into()); + } + "use-datagram" => { + settings.use_datagram = value.get().expect("type checked upstream"); + } + _ => unimplemented!(), + } + } + + fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + let settings = self.settings.lock().unwrap(); + + match pspec.name() { + "server-name" => settings.server_name.to_value(), + "server-address" => settings.server_address.to_string().to_value(), + "server-port" => { + let port = settings.server_port as u32; + port.to_value() + } + "alpn-protocols" => { + let alpns = settings.alpns.iter().map(|v| v.as_str()); + gst::Array::new(alpns).to_value() + } + "caps" => settings.caps.to_value(), + "timeout" => settings.timeout.to_value(), + "secure-connection" => settings.secure_conn.to_value(), + "certificate-file" => { + let certfile = settings.certificate_file.as_ref(); + certfile.and_then(|file| file.to_str()).to_value() + } + "private-key-file" => { + let privkey = settings.private_key_file.as_ref(); + privkey.and_then(|file| file.to_str()).to_value() + } + "use-datagram" => settings.use_datagram.to_value(), + _ => unimplemented!(), + } + } +} + +#[glib::object_subclass] +impl ObjectSubclass for QuinnQuicSrc { + const NAME: &'static str = "GstQuinnQUICSrc"; + type Type = super::QuinnQuicSrc; + type ParentType = gst_base::BaseSrc; +} + +impl BaseSrcImpl for QuinnQuicSrc { + fn is_seekable(&self) -> bool { + false + } + + fn start(&self) -> Result<(), gst::ErrorMessage> { + let settings = self.settings.lock().unwrap(); + let timeout = settings.timeout; + drop(settings); + + let mut state = self.state.lock().unwrap(); + + if let State::Started { .. } = *state { + unreachable!("QuicSrc already started"); + } + + match wait(&self.canceller, self.wait_for_connection(), timeout) { + Ok(Ok((c, s))) => { + *state = State::Started(Started { + connection: c, + stream: s, + }); + + gst::info!(CAT, imp: self, "Started"); + + Ok(()) + } + Ok(Err(e)) | Err(e) => match e { + WaitError::FutureAborted => { + gst::warning!(CAT, imp: self, "Connection aborted"); + Ok(()) + } + WaitError::FutureError(err) => { + gst::error!(CAT, imp: self, "Connection request failed: {}", err); + Err(gst::error_msg!( + gst::ResourceError::Failed, + ["Connection request failed: {}", err] + )) + } + }, + } + } + + fn stop(&self) -> Result<(), gst::ErrorMessage> { + self.cancel(); + + let mut state = self.state.lock().unwrap(); + + if let State::Started(ref mut state) = *state { + let connection = &state.connection; + + connection.close( + CONNECTION_CLOSE_CODE.into(), + CONNECTION_CLOSE_MSG.as_bytes(), + ); + } + + *state = State::Stopped; + + Ok(()) + } + + fn query(&self, query: &mut gst::QueryRef) -> bool { + if let gst::QueryViewMut::Scheduling(q) = query.view_mut() { + q.set( + gst::SchedulingFlags::SEQUENTIAL | gst::SchedulingFlags::BANDWIDTH_LIMITED, + 1, + -1, + 0, + ); + q.add_scheduling_modes(&[gst::PadMode::Pull, gst::PadMode::Push]); + return true; + } + + BaseSrcImplExt::parent_query(self, query) + } + + fn create( + &self, + offset: u64, + buffer: Option<&mut gst::BufferRef>, + length: u32, + ) -> Result { + let data = self.get(offset, u64::from(length)); + + match data { + Ok(bytes) => { + if bytes.is_empty() { + gst::debug!(CAT, imp: self, "End of stream"); + return Err(gst::FlowError::Eos); + } + + if let Some(buffer) = buffer { + if let Err(copied_bytes) = buffer.copy_from_slice(0, bytes.as_ref()) { + buffer.set_size(copied_bytes); + } + Ok(CreateSuccess::FilledBuffer) + } else { + Ok(CreateSuccess::NewBuffer(gst::Buffer::from_slice(bytes))) + } + } + Err(None) => Err(gst::FlowError::Flushing), + Err(Some(err)) => { + gst::error!(CAT, imp: self, "Could not GET: {}", err); + Err(gst::FlowError::Error) + } + } + } + + fn unlock(&self) -> Result<(), gst::ErrorMessage> { + self.cancel(); + Ok(()) + } + + fn caps(&self, filter: Option<&gst::Caps>) -> Option { + let settings = self.settings.lock().unwrap(); + + let mut tmp_caps = settings.caps.clone(); + + gst::debug!(CAT, imp: self, "Advertising our own caps: {:?}", &tmp_caps); + + if let Some(filter_caps) = filter { + gst::debug!( + CAT, + imp: self, + "Intersecting with filter caps: {:?}", + &filter_caps + ); + + tmp_caps = filter_caps.intersect_with_mode(&tmp_caps, gst::CapsIntersectMode::First); + }; + + gst::debug!(CAT, imp: self, "Returning caps: {:?}", &tmp_caps); + + Some(tmp_caps) + } +} + +impl QuinnQuicSrc { + fn get(&self, _offset: u64, length: u64) -> Result> { + let settings = self.settings.lock().unwrap(); + let timeout = settings.timeout; + let use_datagram = settings.use_datagram; + drop(settings); + + let mut state = self.state.lock().unwrap(); + + let (conn, stream) = match *state { + State::Started(Started { + ref connection, + ref mut stream, + }) => (connection, stream), + State::Stopped => { + return Err(Some(gst::error_msg!( + gst::LibraryError::Failed, + ["Cannot get data before start"] + ))); + } + }; + + let future = async { + if use_datagram { + match conn.read_datagram().await { + Ok(bytes) => Ok(bytes), + Err(err) => match err { + ConnectionError::ApplicationClosed(_) + | ConnectionError::ConnectionClosed(_) => Ok(Bytes::new()), + _ => Err(WaitError::FutureError(gst::error_msg!( + gst::ResourceError::Failed, + ["Datagram read error: {}", err] + ))), + }, + } + } else { + let recv = stream.as_mut().unwrap(); + + match recv.read_chunk(length as usize, true).await { + Ok(Some(chunk)) => Ok(chunk.bytes), + Ok(None) => Ok(Bytes::new()), + Err(err) => Err(WaitError::FutureError(gst::error_msg!( + gst::ResourceError::Failed, + ["Stream read error: {}", err] + ))), + } + } + }; + + match wait(&self.canceller, future, timeout) { + Ok(Ok(bytes)) => Ok(bytes), + Ok(Err(e)) | Err(e) => match e { + WaitError::FutureAborted => { + gst::warning!(CAT, imp: self, "Read from stream request aborted"); + Err(None) + } + WaitError::FutureError(e) => { + gst::error!(CAT, imp: self, "Failed to read from stream: {}", e); + Err(Some(e)) + } + }, + } + } + + fn cancel(&self) { + let mut canceller = self.canceller.lock().unwrap(); + + if let Some(c) = canceller.take() { + c.abort() + }; + } + + async fn wait_for_connection(&self) -> Result<(Connection, Option), WaitError> { + let server_addr; + let server_name; + let alpns; + let use_datagram; + let secure_conn; + let cert_file; + let private_key_file; + + { + let settings = self.settings.lock().unwrap(); + + server_addr = make_socket_addr( + format!("{}:{}", settings.server_address, settings.server_port).as_str(), + )?; + + server_name = settings.server_name.clone(); + alpns = settings.alpns.clone(); + use_datagram = settings.use_datagram; + secure_conn = settings.secure_conn; + cert_file = settings.certificate_file.clone(); + private_key_file = settings.private_key_file.clone(); + } + + let endpoint = server_endpoint( + server_addr, + &server_name, + secure_conn, + alpns, + cert_file, + private_key_file, + ) + .map_err(|err| { + WaitError::FutureError(gst::error_msg!( + gst::ResourceError::Failed, + ["Failed to configure endpoint: {}", err] + )) + })?; + + let incoming_conn = endpoint.accept().await.unwrap(); + + let connection = incoming_conn.await.map_err(|err| { + WaitError::FutureError(gst::error_msg!( + gst::ResourceError::Failed, + ["Connection error: {}", err] + )) + })?; + + let stream = if !use_datagram { + let res = connection.accept_uni().await.map_err(|err| { + WaitError::FutureError(gst::error_msg!( + gst::ResourceError::Failed, + ["Failed to open stream: {}", err] + )) + })?; + + Some(res) + } else { + None + }; + + gst::info!( + CAT, + imp: self, + "Remote connection accepted: {}", + connection.remote_address() + ); + + Ok((connection, stream)) + } +} diff --git a/net/quinn/src/quinnquicsrc/mod.rs b/net/quinn/src/quinnquicsrc/mod.rs new file mode 100644 index 00000000..0acf7876 --- /dev/null +++ b/net/quinn/src/quinnquicsrc/mod.rs @@ -0,0 +1,26 @@ +// Copyright (C) 2024, Asymptotic Inc. +// Author: Sanchayan Maity +// +// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0. +// If a copy of the MPL was not distributed with this file, You can obtain one at +// . +// +// SPDX-License-Identifier: MPL-2.0 + +use gst::glib; +use gst::prelude::*; + +mod imp; + +glib::wrapper! { + pub struct QuinnQuicSrc(ObjectSubclass) @extends gst_base::BaseSrc, gst::Element, gst::Object; +} + +pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { + gst::Element::register( + Some(plugin), + "quinnquicsrc", + gst::Rank::MARGINAL, + QuinnQuicSrc::static_type(), + ) +} diff --git a/net/quinn/src/utils.rs b/net/quinn/src/utils.rs new file mode 100644 index 00000000..775d783e --- /dev/null +++ b/net/quinn/src/utils.rs @@ -0,0 +1,310 @@ +// Copyright (C) 2024, Asymptotic Inc. +// Author: Sanchayan Maity +//G +// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0. +// If a copy of the MPL was not distributed with this file, You can obtain one at +// . +// +// SPDX-License-Identifier: MPL-2.0 + +use futures::future; +use futures::prelude::*; +use gst::ErrorMessage; +use once_cell::sync::Lazy; +use quinn::{ClientConfig, Endpoint, ServerConfig}; +use std::error::Error; +use std::fs::File; +use std::io::BufReader; +use std::net::SocketAddr; +use std::path::PathBuf; +use std::sync::{Arc, Mutex}; +use std::time::Duration; +use thiserror::Error; +use tokio::runtime; + +pub const CONNECTION_CLOSE_CODE: u32 = 0; +pub const CONNECTION_CLOSE_MSG: &str = "Stopped"; + +#[derive(Error, Debug)] +pub enum WaitError { + #[error("Future aborted")] + FutureAborted, + #[error("Future returned an error: {0}")] + FutureError(ErrorMessage), +} + +pub static RUNTIME: Lazy = Lazy::new(|| { + runtime::Builder::new_multi_thread() + .enable_all() + .worker_threads(1) + .thread_name("gst-quic-runtime") + .build() + .unwrap() +}); + +pub fn wait( + canceller: &Mutex>, + future: F, + timeout: u32, +) -> Result +where + F: Send + Future, + T: Send + 'static, +{ + let mut canceller_guard = canceller.lock().unwrap(); + let (abort_handle, abort_registration) = future::AbortHandle::new_pair(); + + if canceller_guard.is_some() { + return Err(WaitError::FutureError(gst::error_msg!( + gst::ResourceError::Failed, + ["Old Canceller should not exist"] + ))); + } + + canceller_guard.replace(abort_handle); + drop(canceller_guard); + + let future = async { + if timeout == 0 { + Ok(future.await) + } else { + let res = tokio::time::timeout(Duration::from_secs(timeout.into()), future).await; + + match res { + Ok(r) => Ok(r), + Err(e) => Err(gst::error_msg!( + gst::ResourceError::Read, + ["Request timeout, elapsed: {}", e.to_string()] + )), + } + } + }; + + let future = async { + match future::Abortable::new(future, abort_registration).await { + Ok(Ok(res)) => Ok(res), + + Ok(Err(err)) => Err(WaitError::FutureError(gst::error_msg!( + gst::ResourceError::Failed, + ["Future resolved with an error {:?}", err] + ))), + + Err(future::Aborted) => Err(WaitError::FutureAborted), + } + }; + + let res = RUNTIME.block_on(future); + + canceller_guard = canceller.lock().unwrap(); + *canceller_guard = None; + + res +} + +pub fn make_socket_addr(addr: &str) -> Result { + match addr.parse::() { + Ok(address) => Ok(address), + Err(e) => Err(WaitError::FutureError(gst::error_msg!( + gst::ResourceError::Failed, + ["Invalid address: {}", e] + ))), + } +} + +/* + * Following functions are taken from Quinn documentation/repository + */ +struct SkipServerVerification; + +impl SkipServerVerification { + pub fn new() -> Arc { + Arc::new(Self) + } +} + +impl rustls::client::ServerCertVerifier for SkipServerVerification { + fn verify_server_cert( + &self, + _end_entity: &rustls::Certificate, + _intermediates: &[rustls::Certificate], + _server_name: &rustls::ServerName, + _scts: &mut dyn Iterator, + _ocsp_response: &[u8], + _now: std::time::SystemTime, + ) -> Result { + Ok(rustls::client::ServerCertVerified::assertion()) + } +} + +fn configure_client( + secure_conn: bool, + certificate_file: Option, + private_key_file: Option, + alpns: Vec, +) -> Result> { + let mut crypto = if secure_conn { + let (certs, key) = read_certs_from_file(certificate_file, private_key_file)?; + let mut cert_store = rustls::RootCertStore::empty(); + + for cert in &certs { + cert_store.add(cert)?; + } + + rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(Arc::new(cert_store)) + .with_client_auth_cert(certs, key)? + } else { + rustls::ClientConfig::builder() + .with_safe_defaults() + .with_custom_certificate_verifier(SkipServerVerification::new()) + .with_no_client_auth() + }; + + let alpn_protocols: Vec> = alpns + .iter() + .map(|x| x.as_bytes().to_vec()) + .collect::>(); + crypto.alpn_protocols = alpn_protocols; + crypto.key_log = Arc::new(rustls::KeyLogFile::new()); + + Ok(ClientConfig::new(Arc::new(crypto))) +} + +fn read_certs_from_file( + certificate_file: Option, + private_key_file: Option, +) -> Result<(Vec, rustls::PrivateKey), Box> { + /* + * NOTE: + * + * Certificate file here should correspond to fullchain.pem where + * fullchain.pem = cert.pem + chain.pem. + * fullchain.pem DOES NOT include a CA's Root Certificates. + * + * One typically uses chain.pem (or the first certificate in it) when asked + * for a CA bundle or CA certificate. + * + * One typically uses fullchain.pem when asked for the entire certificate + * chain in a single file. For example, this is the case of modern day + * Apache and nginx. + */ + let cert_file = certificate_file + .clone() + .expect("Expected path to certificates be valid"); + let key_file = private_key_file.expect("Expected path to certificates be valid"); + + let certs: Vec = { + let cert_file = File::open(cert_file.as_path())?; + let mut cert_file_rdr = BufReader::new(cert_file); + let cert_vec = rustls_pemfile::certs(&mut cert_file_rdr)?; + cert_vec.into_iter().map(rustls::Certificate).collect() + }; + + let key: rustls::PrivateKey = { + let key_file = File::open(key_file.as_path())?; + let mut key_file_rdr = BufReader::new(key_file); + + let keys_iter = rustls_pemfile::read_all(&mut key_file_rdr)?; + let key_item = keys_iter + .into_iter() + .next() + .ok_or("Certificate should have at least one private key")?; + + match key_item { + rustls_pemfile::Item::RSAKey(key) => rustls::PrivateKey(key), + rustls_pemfile::Item::PKCS8Key(key) => rustls::PrivateKey(key), + _ => unimplemented!(), + } + }; + + Ok((certs, key)) +} + +fn configure_server( + server_name: &str, + secure_conn: bool, + certificate_file: Option, + private_key_file: Option, + alpns: Vec, +) -> Result<(ServerConfig, Vec), Box> { + let (certs, key) = if secure_conn { + read_certs_from_file(certificate_file, private_key_file)? + } else { + let cert = rcgen::generate_simple_self_signed(vec![server_name.into()]).unwrap(); + let cert_der = cert.serialize_der().unwrap(); + let priv_key = cert.serialize_private_key_der(); + let priv_key = rustls::PrivateKey(priv_key); + let cert_chain = vec![rustls::Certificate(cert_der)]; + + (cert_chain, priv_key) + }; + + let mut crypto = if secure_conn { + let mut cert_store = rustls::RootCertStore::empty(); + for cert in &certs { + cert_store.add(cert)?; + } + + let auth_client = rustls::server::AllowAnyAuthenticatedClient::new(cert_store); + rustls::ServerConfig::builder() + .with_safe_defaults() + .with_client_cert_verifier(Arc::new(auth_client)) + .with_single_cert(certs.clone(), key) + } else { + rustls::ServerConfig::builder() + .with_safe_defaults() + .with_no_client_auth() + .with_single_cert(certs.clone(), key) + }?; + + let alpn_protocols: Vec> = alpns + .iter() + .map(|x| x.as_bytes().to_vec()) + .collect::>(); + crypto.alpn_protocols = alpn_protocols; + crypto.key_log = Arc::new(rustls::KeyLogFile::new()); + let mut server_config = ServerConfig::with_crypto(Arc::new(crypto)); + + Arc::get_mut(&mut server_config.transport) + .unwrap() + .max_concurrent_bidi_streams(0_u8.into()) + .max_concurrent_uni_streams(1_u8.into()); + + Ok((server_config, certs)) +} + +pub fn server_endpoint( + server_addr: SocketAddr, + server_name: &str, + secure_conn: bool, + alpns: Vec, + certificate_file: Option, + private_key_file: Option, +) -> Result> { + let (server_config, _) = configure_server( + server_name, + secure_conn, + certificate_file, + private_key_file, + alpns, + )?; + let endpoint = Endpoint::server(server_config, server_addr)?; + + Ok(endpoint) +} + +pub fn client_endpoint( + client_addr: SocketAddr, + secure_conn: bool, + alpns: Vec, + certificate_file: Option, + private_key_file: Option, +) -> Result> { + let client_cfg = configure_client(secure_conn, certificate_file, private_key_file, alpns)?; + let mut endpoint = Endpoint::client(client_addr)?; + + endpoint.set_default_client_config(client_cfg); + + Ok(endpoint) +} diff --git a/net/quinn/tests/quinnquic.rs b/net/quinn/tests/quinnquic.rs new file mode 100644 index 00000000..d8d52b87 --- /dev/null +++ b/net/quinn/tests/quinnquic.rs @@ -0,0 +1,112 @@ +// Copyright (C) 2024, Asymptotic Inc. +// Author: Sanchayan Maity +// +// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0. +// If a copy of the MPL was not distributed with this file, You can obtain one at +// . +// +// SPDX-License-Identifier: MPL-2.0 + +use gst::prelude::*; +use serial_test::serial; +use std::thread; + +fn init() { + use std::sync::Once; + static INIT: Once = Once::new(); + + INIT.call_once(|| { + gst::init().unwrap(); + gstquinn::plugin_register_static().expect("QUIC source sink send receive tests"); + }); +} + +fn make_buffer(content: &[u8]) -> gst::Buffer { + let mut buf = gst::Buffer::from_slice(content.to_owned()); + buf.make_mut().set_pts(gst::ClockTime::from_mseconds(200)); + buf +} + +#[test] +#[serial] +fn test_send_receive_without_datagram() { + init(); + + let content = "Hello, world!\n".as_bytes(); + + thread::spawn(move || { + let mut h1 = gst_check::Harness::new_empty(); + h1.add_parse("quinnquicsink secure-connection=false"); + + h1.set_src_caps(gst::Caps::builder("text/plain").build()); + + h1.play(); + + assert!(h1.push(make_buffer(content)) == Ok(gst::FlowSuccess::Ok)); + + h1.push_event(gst::event::Eos::new()); + + h1.element().unwrap().set_state(gst::State::Null).unwrap(); + + drop(h1); + }); + + let mut h2 = gst_check::Harness::new_empty(); + h2.add_parse("quinnquicsrc secure-connection=false"); + + h2.play(); + + let buf = h2.pull_until_eos().unwrap().unwrap(); + + assert_eq!( + content, + buf.into_mapped_buffer_readable().unwrap().as_slice() + ); + + h2.element().unwrap().set_state(gst::State::Null).unwrap(); + + drop(h2); +} + +#[test] +#[serial] +fn test_send_receive_with_datagram() { + init(); + + let content = "Hello, world!\n".as_bytes(); + + // Use a different port address compared to the default that will be used + // in the other test. We get a address already in use error otherwise. + thread::spawn(move || { + let mut h1 = gst_check::Harness::new_empty(); + h1.add_parse("quinnquicsrc use-datagram=true server-address=127.0.0.1 server-port=6000 secure-connection=false"); + + h1.play(); + + let buf = h1.pull_until_eos().unwrap().unwrap(); + + assert_eq!( + content, + buf.into_mapped_buffer_readable().unwrap().as_slice() + ); + + h1.element().unwrap().set_state(gst::State::Null).unwrap(); + + drop(h1); + }); + + let mut h2 = gst_check::Harness::new_empty(); + h2.add_parse("quinnquicsink use-datagram=true client-address=127.0.0.1 client-port=6001 server-address=127.0.0.1 server-port=6000 secure-connection=false"); + + h2.set_src_caps(gst::Caps::builder("text/plain").build()); + + h2.play(); + + assert!(h2.push(make_buffer(content)) == Ok(gst::FlowSuccess::Ok)); + + h2.push_event(gst::event::Eos::new()); + + h2.element().unwrap().set_state(gst::State::Null).unwrap(); + + drop(h2); +}