From eeaf4aa681ffbe43e15f62f1ef363f7e0acdbe73 Mon Sep 17 00:00:00 2001 From: videogame hacker Date: Fri, 10 Mar 2023 06:33:01 +0000 Subject: [PATCH] Initial commit --- CMakeLists.txt | 60 + README.md | 5 + char-rtc.c | 21 + data/.keepme | 0 data/locale/en-US.ini | 4 + rtc-backend/.gitignore | 1 + rtc-backend/Cargo.lock | 2736 ++++++++++++++++++++++++++++++++ rtc-backend/Cargo.toml | 25 + rtc-backend/build.rs | 26 + rtc-backend/cbindgen.toml | 1 + rtc-backend/src/ffi/mod.rs | 2 + rtc-backend/src/ffi/obs_log.rs | 7 + rtc-backend/src/ffi/output.rs | 174 ++ rtc-backend/src/lib.rs | 22 + rtc-backend/src/obs_log.rs | 127 ++ rtc-backend/src/output.rs | 443 ++++++ rtc-backend/src/whip.rs | 93 ++ whip-output.c | 232 +++ whip-output.h | 17 + whip-service.c | 100 ++ whip-service.h | 5 + 21 files changed, 4101 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 char-rtc.c create mode 100644 data/.keepme create mode 100644 data/locale/en-US.ini create mode 100644 rtc-backend/.gitignore create mode 100644 rtc-backend/Cargo.lock create mode 100644 rtc-backend/Cargo.toml create mode 100644 rtc-backend/build.rs create mode 100644 rtc-backend/cbindgen.toml create mode 100644 rtc-backend/src/ffi/mod.rs create mode 100644 rtc-backend/src/ffi/obs_log.rs create mode 100644 rtc-backend/src/ffi/output.rs create mode 100644 rtc-backend/src/lib.rs create mode 100644 rtc-backend/src/obs_log.rs create mode 100644 rtc-backend/src/output.rs create mode 100644 rtc-backend/src/whip.rs create mode 100644 whip-output.c create mode 100644 whip-output.h create mode 100644 whip-service.c create mode 100644 whip-service.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..79465a7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,60 @@ +project(char-rtc-obs) + +find_package(Corrosion QUIET) +if(NOT Corrosion_FOUND) + include(FetchContent) + FetchContent_Declare( + Corrosion + GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git + GIT_TAG v0.3.1) + FetchContent_MakeAvailable(Corrosion) +endif() + +set(CHAR_RTC_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") + +corrosion_import_crate(MANIFEST_PATH rtc-backend/Cargo.toml) +corrosion_set_env_vars( + char-rtc-backend "CHAR_RTC_GENERATED_DIR=${CHAR_RTC_GENERATED_DIR}" + "CHAR_RTC_OBS_VERSION=${OBS_VERSION_CANONICAL}") + +# Force dependent crates to link against the correct deployment target +if(OS_MACOS) + corrosion_set_env_vars( + char-rtc-backend + "CFLAGS_aarch64_apple_darwin=-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}" + "CFLAGS_x86_64_apple_darwin=-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}" + ) + corrosion_add_target_rustflags( + char-rtc-backend + -Clink-arg=-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}) +endif() + +add_library(char-rtc-obs MODULE) + +target_sources(char-rtc-obs PRIVATE char-rtc.c whip-service.c whip-service.h whip-output.c whip-output.h + "${CHAR_RTC_GENERATED_DIR}/bindings.h") +set_source_files_properties("${CHAR_RTC_GENERATED_DIR}/bindings.h" + PROPERTIES GENERATED TRUE) + +target_link_libraries(char-rtc-obs OBS::libobs char-rtc-backend) +target_include_directories(char-rtc-obs PRIVATE ${CHAR_RTC_GENERATED_DIR}) + +if(OS_WINDOWS) + set(MODULE_DESCRIPTION "live.umm.gay module") + configure_file("${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in" + char-rtc-obs.rc) + target_sources(char-rtc-obs PRIVATE char-rtc-obs.rc) +endif() + +if(OS_MACOS) + find_library(COREFOUNDATION CoreFoundation) + find_library(SECURITY_FRAMEWORK Security) + find_library(SYSTEMCONFIGURATION SystemConfiguration) + mark_as_advanced(COREFOUNDATION SECURITY_FRAMEWOR SYSTEMCONFIGURATION) + target_link_libraries(char-rtc-obs ${COREFOUNDATION} ${SECURITY_FRAMEWORK} + ${SYSTEMCONFIGURATION}) +endif() + +set_target_properties(char-rtc-obs PROPERTIES FOLDER "plugins") + +setup_plugin_target(char-rtc-obs data) diff --git a/README.md b/README.md new file mode 100644 index 0000000..9c0dbeb --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# char-rtc-obs + +OBS plugin to stream via WebRTC (using `webrtc-rs`) to `live.umm.gay` + +you clone this into your `obs-studio/plugins/` folder :) diff --git a/char-rtc.c b/char-rtc.c new file mode 100644 index 0000000..2f68b88 --- /dev/null +++ b/char-rtc.c @@ -0,0 +1,21 @@ +#include + +#include "whip-output.h" +#include "whip-service.h" + +OBS_DECLARE_MODULE() +OBS_MODULE_USE_DEFAULT_LOCALE("char-rtc-obs", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return obs_module_text("Module.Description"); +} + +MODULE_EXPORT bool obs_module_load() +{ + char_rtc_install_logger(); + + register_whip_output(); + register_whip_service(); + + return true; +} diff --git a/data/.keepme b/data/.keepme new file mode 100644 index 0000000..e69de29 diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini new file mode 100644 index 0000000..bb6aa8b --- /dev/null +++ b/data/locale/en-US.ini @@ -0,0 +1,4 @@ +Module.Description="Publish to live.umm.gay using OBS Studio" +Output.Name="live.umm.gay Output" +Service.Name="live.umm.gay Service" +Service.BearerToken="Bearer Token" diff --git a/rtc-backend/.gitignore b/rtc-backend/.gitignore new file mode 100644 index 0000000..0b42d2d --- /dev/null +++ b/rtc-backend/.gitignore @@ -0,0 +1 @@ +/target diff --git a/rtc-backend/Cargo.lock b/rtc-backend/Cargo.lock new file mode 100644 index 0000000..fee77e8 --- /dev/null +++ b/rtc-backend/Cargo.lock @@ -0,0 +1,2736 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aead" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +dependencies = [ + "generic-array", +] + +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array", + "rand_core 0.6.3", +] + +[[package]] +name = "aes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +dependencies = [ + "aes-soft", + "aesni", + "cipher 0.2.5", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher 0.3.0", + "cpufeatures", + "opaque-debug", +] + +[[package]] +name = "aes-gcm" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" +dependencies = [ + "aead 0.3.2", + "aes 0.6.0", + "cipher 0.2.5", + "ctr 0.6.0", + "ghash 0.3.1", + "subtle", +] + +[[package]] +name = "aes-gcm" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +dependencies = [ + "aead 0.4.3", + "aes 0.7.5", + "cipher 0.3.0", + "ctr 0.8.0", + "ghash 0.4.4", + "subtle", +] + +[[package]] +name = "aes-soft" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +dependencies = [ + "cipher 0.2.5", + "opaque-debug", +] + +[[package]] +name = "aesni" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +dependencies = [ + "cipher 0.2.5", + "opaque-debug", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3" + +[[package]] +name = "arc-swap" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164" + +[[package]] +name = "asn1-rs" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ff05a702273012438132f449575dbc804e27b2f3cbe3069aa237d26c98fa33" +dependencies = [ + "asn1-rs-derive 0.1.0", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf6690c370453db30743b373a60ba498fc0d6d83b11f4abfd87a84a075db5dd4" +dependencies = [ + "asn1-rs-derive 0.4.0", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "base64ct" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-modes" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0" +dependencies = [ + "block-padding", + "cipher 0.2.5", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cbindgen" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6358dedf60f4d9b8db43ad187391afe959746101346fe51bb978126bec61dfb" +dependencies = [ + "clap", + "heck", + "indexmap", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", + "tempfile", + "toml", +] + +[[package]] +name = "cc" +version = "1.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" + +[[package]] +name = "ccm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca1a8fbc20b50ac9673ff014abfb2b5f4085ee1a850d408f14a159c5853ac7" +dependencies = [ + "aead 0.3.2", + "cipher 0.2.5", + "subtle", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "char-rtc-backend" +version = "0.1.0" +dependencies = [ + "anyhow", + "base64", + "bytes", + "cbindgen", + "env_logger", + "if-addrs", + "link_args", + "log", + "reqwest", + "serde", + "tokio", + "webrtc", + "webrtc-mdns", +] + +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + +[[package]] +name = "clap" +version = "3.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +dependencies = [ + "atty", + "bitflags", + "clap_lex", + "indexmap", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "const-oid" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "cpuid-bool" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" + +[[package]] +name = "crc" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.3", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "ctr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +dependencies = [ + "cipher 0.2.5", +] + +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher 0.3.0", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "darling" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "data-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" + +[[package]] +name = "der" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dd2ae565c0a381dde7fade45fce95984c568bdcb4700a4fdbe3175e0380b2f" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe398ac75057914d7d07307bf67dc7f3f574a26783b4fc7805a20ffa9f506e82" +dependencies = [ + "asn1-rs 0.3.1", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "der-parser" +version = "8.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1" +dependencies = [ + "asn1-rs 0.5.1", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "derive_builder" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" +dependencies = [ + "derive_builder_core", + "syn", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.0", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.6", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.3", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "log", +] + +[[package]] +name = "fastrand" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2" +dependencies = [ + "instant", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.3", + "subtle", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" + +[[package]] +name = "futures-executor" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" + +[[package]] +name = "futures-macro" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" + +[[package]] +name = "futures-task" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" + +[[package]] +name = "futures-util" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.10.2+wasi-snapshot-preview1", +] + +[[package]] +name = "ghash" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" +dependencies = [ + "opaque-debug", + "polyval 0.4.5", +] + +[[package]] +name = "ghash" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +dependencies = [ + "opaque-debug", + "polyval 0.5.3", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core 0.6.3", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9de88456263e249e241fcd211d3954e2c9b0ef7ccfc235a444eb367cae3689" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac 0.12.1", +] + +[[package]] +name = "hmac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" +dependencies = [ + "crypto-mac 0.10.1", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac 0.11.1", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "http" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.1", +] + +[[package]] +name = "http-body" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa 0.4.8", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "rustls 0.20.7", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "if-addrs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "interceptor" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ffaa4d24f546a18eaeee91f7b2c52e080e20e285e43cd7c27a527b4712cfdad" +dependencies = [ + "async-trait", + "bytes", + "log", + "rand", + "rtcp", + "rtp", + "thiserror", + "tokio", + "waitgroup", + "webrtc-srtp", + "webrtc-util", +] + +[[package]] +name = "ipnet" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745" + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "link_args" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7721e472624c9aaad27a5eb6b7c9c6045c7a396f2efb6dabaec1b640d5e89b" + +[[package]] +name = "lock_api" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "nix" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "nom" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +dependencies = [ + "memchr", + "minimal-lexical", + "version_check", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "oid-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e20717fa0541f39bd146692035c37bedfa532b3e5071b35761082407546b2a" +dependencies = [ + "asn1-rs 0.3.1", +] + +[[package]] +name = "oid-registry" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d4bda43fd1b844cbc6e6e54b5444e2b1bc7838bce59ad205902cccbb26d6761" +dependencies = [ + "asn1-rs 0.5.1", +] + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "p384" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" +dependencies = [ + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pem" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9a3b09a20e374558580a4914d3b7d89bd61b954a5a5e1dcbea98753addb1947" +dependencies = [ + "base64", +] + +[[package]] +name = "pem-rfc7468" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "polyval" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" +dependencies = [ + "cpuid-bool", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom 0.2.4", +] + +[[package]] +name = "rcgen" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" +dependencies = [ + "pem", + "ring", + "time", + "x509-parser", + "yasna", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f242f1488a539a79bac6dbe7c8609ae43b7914b7736210f239a37cccb32525" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "rustls 0.20.7", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac 0.12.1", + "zeroize", +] + +[[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", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rtcp" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11171e7e37998dcf54a9e9d4a6e2e1932c994424c7d39bc6349fed1424c45c3" +dependencies = [ + "bytes", + "thiserror", + "webrtc-util", +] + +[[package]] +name = "rtp" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a095411ff00eed7b12e4c6a118ba984d113e1079582570d56a5ee723f11f80" +dependencies = [ + "async-trait", + "bytes", + "rand", + "serde", + "thiserror", + "webrtc-util", +] + +[[package]] +name = "rusticata-macros" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c52377bb2288aa522a0c8208947fada1e0c76397f108cc08f57efe6077b50d" +dependencies = [ + "nom", +] + +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64", + "log", + "ring", + "sct 0.6.1", + "webpki 0.21.4", +] + +[[package]] +name = "rustls" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +dependencies = [ + "log", + "ring", + "sct 0.7.0", + "webpki 0.22.0", +] + +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sdp" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d22a5ef407871893fd72b4562ee15e4742269b173959db4b8df6f538c414e13" +dependencies = [ + "rand", + "substring", + "thiserror", + "url", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "serde" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c059c05b48c5c0067d4b4b2b4f0732dd65feb52daf7e0ea09cd87e7dadc1af79" +dependencies = [ + "itoa 1.0.1", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.1", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.6", + "rand_core 0.6.3", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "stun" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25" +dependencies = [ + "base64", + "crc", + "lazy_static", + "md-5", + "rand", + "ring", + "subtle", + "thiserror", + "tokio", + "url", + "webrtc-util", +] + +[[package]] +name = "substring" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ee6433ecef213b2e72f587ef64a2f5943e7cd16fbd82dbe8bc07486c534c86" +dependencies = [ + "autocfg", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae548ec36cf198c0ef7710d3c230987c2d6d7bd98ad6edc0274462724c585ce" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +dependencies = [ + "itoa 1.0.1", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls 0.20.7", + "tokio", + "webpki 0.22.0", +] + +[[package]] +name = "tokio-util" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "turn" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4712ee30d123ec7ae26d1e1b218395a16c87cdbaf4b3925d170d684af62ea5e8" +dependencies = [ + "async-trait", + "base64", + "futures", + "log", + "md-5", + "rand", + "ring", + "stun", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "uuid" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" +dependencies = [ + "getrandom 0.2.4", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "waitgroup" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1f50000a783467e6c0200f9d10642f4bc424e39efc1b770203e88b488f79292" +dependencies = [ + "atomic-waker", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki 0.22.0", +] + +[[package]] +name = "webrtc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3bc9049bdb2cea52f5fd4f6f728184225bdb867ed0dc2410eab6df5bdd67bb" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "hex", + "interceptor", + "lazy_static", + "log", + "rand", + "rcgen", + "regex", + "ring", + "rtcp", + "rtp", + "rustls 0.19.1", + "sdp", + "serde", + "serde_json", + "sha2 0.10.6", + "stun", + "thiserror", + "time", + "tokio", + "turn", + "url", + "waitgroup", + "webrtc-data", + "webrtc-dtls", + "webrtc-ice", + "webrtc-mdns", + "webrtc-media", + "webrtc-sctp", + "webrtc-srtp", + "webrtc-util", +] + +[[package]] +name = "webrtc-data" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef36a4d12baa6e842582fe9ec16a57184ba35e1a09308307b67d43ec8883100" +dependencies = [ + "bytes", + "derive_builder", + "log", + "thiserror", + "tokio", + "webrtc-sctp", + "webrtc-util", +] + +[[package]] +name = "webrtc-dtls" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7021987ae0a2ed6c8cd33f68e98e49bb6e74ffe9543310267b48a1bbe3900e5f" +dependencies = [ + "aes 0.6.0", + "aes-gcm 0.8.0", + "async-trait", + "bincode", + "block-modes", + "byteorder", + "ccm", + "curve25519-dalek", + "der-parser 8.1.0", + "elliptic-curve", + "hkdf", + "hmac 0.10.1", + "log", + "oid-registry 0.6.0", + "p256", + "p384", + "rand", + "rand_core 0.6.3", + "rcgen", + "ring", + "rustls 0.19.1", + "sec1", + "serde", + "sha-1", + "sha2 0.9.9", + "signature", + "subtle", + "thiserror", + "tokio", + "webpki 0.21.4", + "webrtc-util", + "x25519-dalek", + "x509-parser", +] + +[[package]] +name = "webrtc-ice" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494483fbb2f5492620871fdc78b084aed8807377f6e3fe88b2e49f0a9c9c41d7" +dependencies = [ + "arc-swap", + "async-trait", + "crc", + "log", + "rand", + "serde", + "serde_json", + "stun", + "thiserror", + "tokio", + "turn", + "url", + "uuid", + "waitgroup", + "webrtc-mdns", + "webrtc-util", +] + +[[package]] +name = "webrtc-mdns" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f08dfd7a6e3987e255c4dbe710dde5d94d0f0574f8a21afa95d171376c143106" +dependencies = [ + "log", + "socket2", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-media" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2a3c157a040324e5049bcbd644ffc9079e6738fa2cfab2bcff64e5cc4c00d7" +dependencies = [ + "byteorder", + "bytes", + "derive_builder", + "displaydoc", + "rand", + "rtp", + "thiserror", + "webrtc-util", +] + +[[package]] +name = "webrtc-sctp" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d47adcd9427eb3ede33d5a7f3424038f63c965491beafcc20bc650a2f6679c0" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "crc", + "log", + "rand", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-srtp" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6183edc4c1c6c0175f8812eefdce84dfa0aea9c3ece71c2bf6ddd3c964de3da5" +dependencies = [ + "aead 0.4.3", + "aes 0.7.5", + "aes-gcm 0.9.4", + "async-trait", + "byteorder", + "bytes", + "ctr 0.8.0", + "hmac 0.11.0", + "log", + "rtcp", + "rtp", + "sha-1", + "subtle", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-util" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f1db1727772c05cf7a2cfece52c3aca8045ca1e176cd517d323489aa3c6d87" +dependencies = [ + "async-trait", + "bitflags", + "bytes", + "cc", + "ipnet", + "lazy_static", + "libc", + "log", + "nix", + "rand", + "thiserror", + "tokio", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi", +] + +[[package]] +name = "x25519-dalek" +version = "2.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5da623d8af10a62342bcbbb230e33e58a63255a58012f8653c578e54bab48df" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.3", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9bace5b5589ffead1afb76e43e34cff39cd0f3ce7e170ae0c29e53b88eb1c" +dependencies = [ + "asn1-rs 0.3.1", + "base64", + "data-encoding", + "der-parser 7.0.0", + "lazy_static", + "nom", + "oid-registry 0.4.0", + "ring", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "yasna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346d34a236c9d3e5f3b9b74563f238f955bbd05fa0b8b4efa53c130c43982f4c" +dependencies = [ + "time", +] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81e8f13fef10b63c06356d65d416b070798ddabcadc10d3ece0c5be9b3c7eddb" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/rtc-backend/Cargo.toml b/rtc-backend/Cargo.toml new file mode 100644 index 0000000..69d5be9 --- /dev/null +++ b/rtc-backend/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "char-rtc-backend" +version = "0.1.0" +edition = "2021" +build = "build.rs" + +[dependencies] +webrtc = "0.6.0" +webrtc-mdns = "0.5.2" +tokio = "1.16.1" +anyhow = "1.0.45" +bytes = "1" +serde = { version = "1.0", features = ["derive"] } +base64 = "0.13.0" +reqwest = { version = "0.11.7", default-features = false, features = ["json", "rustls-tls"] } +log = { version = "0.4.17", features = ["std"] } +link_args = "0.6.0" +env_logger = { version = "0.10.0", default-features = false } +if-addrs = "0.7.0" + +[lib] +crate-type=["staticlib"] + +[build-dependencies] +cbindgen = "0.24.3" diff --git a/rtc-backend/build.rs b/rtc-backend/build.rs new file mode 100644 index 0000000..95faac8 --- /dev/null +++ b/rtc-backend/build.rs @@ -0,0 +1,26 @@ +use std::{env, path::PathBuf}; + +fn main() { + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let bindings_dir = env::var("CHAR_RTC_GENERATED_DIR").ok(); + let version = env::var("CHAR_RTC_OBS_VERSION").unwrap_or_else(|_| "0.0.0".into()); + + // Make libobs version available + println!("cargo:rustc-env=OBS_VERSION={version}"); + + let config = cbindgen::Config { + language: cbindgen::Language::C, + ..Default::default() + }; + + match cbindgen::generate_with_config(crate_dir, config) { + Ok(bindings) => { + if let Some(bindings_dir) = bindings_dir { + let bindings_dir: PathBuf = bindings_dir.into(); + bindings.write_to_file(bindings_dir.join("bindings.h")); + } + } + Err(cbindgen::Error::ParseSyntaxError { .. }) => (), // ignore in favor of cargo's syntax check + Err(err) => panic!("{:?}", err), + }; +} diff --git a/rtc-backend/cbindgen.toml b/rtc-backend/cbindgen.toml new file mode 100644 index 0000000..876e404 --- /dev/null +++ b/rtc-backend/cbindgen.toml @@ -0,0 +1 @@ +language = "C" diff --git a/rtc-backend/src/ffi/mod.rs b/rtc-backend/src/ffi/mod.rs new file mode 100644 index 0000000..42613ff --- /dev/null +++ b/rtc-backend/src/ffi/mod.rs @@ -0,0 +1,2 @@ +mod obs_log; +mod output; diff --git a/rtc-backend/src/ffi/obs_log.rs b/rtc-backend/src/ffi/obs_log.rs new file mode 100644 index 0000000..579a68b --- /dev/null +++ b/rtc-backend/src/ffi/obs_log.rs @@ -0,0 +1,7 @@ +use crate::obs_log::OBSLogger; + +#[no_mangle] +pub extern "C" fn char_rtc_install_logger() { + OBSLogger::install(); + log::info!("webrtc plugin preamble initialized") +} diff --git a/rtc-backend/src/ffi/output.rs b/rtc-backend/src/ffi/output.rs new file mode 100644 index 0000000..e2c0eef --- /dev/null +++ b/rtc-backend/src/ffi/output.rs @@ -0,0 +1,174 @@ +use crate::output::{EncodedPacket, EncodedPacketType, OutputStream, OutputStreamError}; +use anyhow::Result; +use log::{error, info}; +use std::{ + os::raw::{c_char, c_void}, + slice, + time::Duration, +}; +use tokio::runtime::Runtime; + +pub struct RTCOutput { + stream: OutputStream, + runtime: Runtime, +} +/// cbindgen:prefix-with-name +#[repr(C)] +pub enum RTCOutputError { + ConnectFailed, + NetworkError, +} + +impl From for RTCOutputError { + fn from(ose: OutputStreamError) -> Self { + match ose { + OutputStreamError::ConnectFailed => RTCOutputError::ConnectFailed, + OutputStreamError::NetworkError => RTCOutputError::NetworkError, + OutputStreamError::WriteError(_) => RTCOutputError::NetworkError, + } + } +} + +// You must call `char_rtc_output_free` on the returned value +#[no_mangle] +pub extern "C" fn char_rtc_output_new() -> *mut RTCOutput { + (|| -> Result<*mut RTCOutput> { + let runtime = tokio::runtime::Runtime::new()?; + let stream = runtime.block_on(OutputStream::new())?; + Ok(Box::into_raw(Box::new(RTCOutput { stream, runtime }))) + })() + .unwrap_or_else(|e| { + error!("Unable to create whip output: {e:?}"); + std::ptr::null_mut::() + }) +} + +#[no_mangle] +pub unsafe extern "C" fn char_rtc_output_free(output: *mut RTCOutput) { + info!("Freeing whip output"); + if !output.is_null() { + drop(Box::from_raw(output)); + } +} + +#[no_mangle] +pub unsafe extern "C" fn char_rtc_output_bytes_sent(output: *const RTCOutput) -> u64 { + let output = output.as_ref().unwrap(); + output.stream.bytes_sent() +} + +#[no_mangle] +pub unsafe extern "C" fn char_rtc_output_dropped_frames(output: *const RTCOutput) -> i32 { + let output = output.as_ref().unwrap(); + output.stream.dropped_frames() +} + +#[no_mangle] +pub unsafe extern "C" fn char_rtc_output_congestion(output: *const RTCOutput) -> f64 { + let output = output.as_ref().unwrap(); + output.stream.congestion() +} + +#[no_mangle] +pub unsafe extern "C" fn char_rtc_output_connect_time_ms(output: *const RTCOutput) -> i32 { + let output = output.as_ref().unwrap(); + output.stream.connect_time().as_millis() as i32 +} + +#[no_mangle] +pub unsafe extern "C" fn char_rtc_output_connect( + output: *const RTCOutput, + url: *const c_char, + bearer_token: *const c_char, +) { + let output = output.as_ref().unwrap(); + + let url = std::ffi::CStr::from_ptr(url).to_str().unwrap().to_owned(); + let bearer_token = if !bearer_token.is_null() { + Some( + std::ffi::CStr::from_ptr(bearer_token) + .to_str() + .unwrap() + .to_owned(), + ) + } else { + None + }; + + output.runtime.spawn(async move { + output.stream.connect(&url, bearer_token.as_deref()).await; + }); +} + +// Once closed, you cannot call `char_rtc_output_connect` again +#[no_mangle] +pub unsafe extern "C" fn char_rtc_output_close(output: *const RTCOutput) { + let output = output.as_ref().unwrap(); + + info!("Closing whip output"); + output + .runtime + .block_on(async { + info!("I'm on a thread!"); + output.stream.close().await + }) + .unwrap_or_else(|e| error!("Failed closing whip output: {e:?}")) +} + +/// Write an audio or video packet to the whip output +#[no_mangle] +pub unsafe extern "C" fn char_rtc_output_write( + output: *const RTCOutput, + data: *const u8, + size: usize, + duration: u64, + is_audio: bool, +) -> bool { + let output = output.as_ref().unwrap(); + + let slice: &[u8] = slice::from_raw_parts(data, size); + let encoded_packet = EncodedPacket { + data: slice.to_owned(), + duration: Duration::from_micros(duration), + typ: if is_audio { + EncodedPacketType::Audio + } else { + EncodedPacketType::Video + }, + }; + + output + .stream + .write(encoded_packet) + .map(|_| true) + .unwrap_or_else(|e| { + error!("Failed to write packets to whip output: {e:?}"); + false + }) +} + +pub struct ErrorCallbackUserdata(*mut c_void); +unsafe impl Send for ErrorCallbackUserdata {} +unsafe impl Sync for ErrorCallbackUserdata {} + +impl ErrorCallbackUserdata { + fn as_ptr(&self) -> *mut c_void { + self.0 + } +} + +type RTCOutputErrorCallback = unsafe extern "C" fn(user_data: *mut c_void, error: RTCOutputError); + +#[no_mangle] +pub unsafe extern "C" fn char_rtc_output_set_error_callback( + output: *const RTCOutput, + cb: RTCOutputErrorCallback, + user_data: *mut c_void, +) { + let output = output.as_ref().unwrap(); + + let user_data = ErrorCallbackUserdata(user_data); + output + .stream + .set_error_callback(Box::new(move |error| cb(user_data.as_ptr(), error.into()))) +} diff --git a/rtc-backend/src/lib.rs b/rtc-backend/src/lib.rs new file mode 100644 index 0000000..d5dce73 --- /dev/null +++ b/rtc-backend/src/lib.rs @@ -0,0 +1,22 @@ +mod ffi; +mod obs_log; +mod output; +mod whip; + +// Manually configure the linked runtimes for windows due to rust defaulting to msvcrt for both debug and release + +#[cfg(all(not(debug_assertions), target_env = "msvc"))] +link_args::windows! { + unsafe { + default_lib("kernel32.lib", "vcruntime.lib", "msvcrtd", "ntdll.lib", "iphlpapi.lib"); + no_default_lib("msvcrtd", "vcruntimed.lib"); + } +} + +#[cfg(all(debug_assertions, target_env = "msvc"))] +link_args::windows! { + unsafe { + default_lib("kernel32.lib", "vcruntimed.lib", "msvcrtd", "ntdll.lib", "iphlpapi.lib"); + no_default_lib("msvcrt", "vcruntime.lib"); + } +} diff --git a/rtc-backend/src/obs_log.rs b/rtc-backend/src/obs_log.rs new file mode 100644 index 0000000..bb72bce --- /dev/null +++ b/rtc-backend/src/obs_log.rs @@ -0,0 +1,127 @@ +use env_logger::filter::{Builder, Filter}; +use log::{Level, Log}; +use std::{ffi::CStr, sync::atomic::AtomicBool, sync::atomic::Ordering}; + +static DEBUG_WHIP: AtomicBool = AtomicBool::new(false); + +pub(crate) fn debug_whip() -> bool { + DEBUG_WHIP.load(Ordering::Relaxed) +} + +/// cbindgen:ignore +mod obs { + use std::os::raw::c_char; + use std::os::raw::c_int; + + #[repr(C)] + #[derive(Debug)] + pub(super) struct obs_cmdline_args { + pub(super) argc: c_int, + pub(super) argv: *const *const c_char, + } + + #[link(name = "libobs")] + extern "C" { + pub(super) fn blog(log_level: i32, format: *const c_char, ...); + pub(super) fn obs_get_cmdline_args() -> obs_cmdline_args; + } +} + +pub struct OBSLogger { + log_filter: Filter, +} + +impl OBSLogger { + pub fn install() { + // Pull our RUST_LOG in case they are using that + let mut log_filter: String = std::env::var("RUST_LOG").unwrap_or_else(|_| "INFO".into()); + + unsafe { + let raw_args = obs::obs_get_cmdline_args(); + let mut args = std::slice::from_raw_parts(raw_args.argv, raw_args.argc as usize) + .iter() + .map(|arg| CStr::from_ptr(*arg).to_string_lossy()); + + while let Some(arg) = args.next() { + match arg.as_ref() { + "--debug-webrtc-whip" => { + DEBUG_WHIP.store(true, Ordering::Relaxed); + } + "--debug-webrtc-filter" => { + if let Some(filter) = args.next() { + log_filter = filter.into(); + } + } + _ => {} + } + } + let mut filter_builder = Builder::new(); + log::set_boxed_logger(Box::new(Self { + log_filter: filter_builder.parse(&log_filter).build(), + })) + .expect("Logger already set"); + + log::set_max_level(log::LevelFilter::Trace); + } + } +} + +impl Log for OBSLogger { + fn enabled(&self, metadata: &log::Metadata) -> bool { + self.log_filter.enabled(metadata) + } + + fn log(&self, record: &log::Record) { + if !self.log_filter.matches(record) { + return; + } + + let level = record.level(); + let log_level = match level { + Level::Trace => (300, "[T] "), + Level::Debug => (300, "[D] "), + Level::Info => (300, ""), + Level::Warn => (200, ""), + Level::Error => (100, ""), + }; + + let message_prefix = format!( + "{}[{}:{}]", + log_level.1, + if record.target().is_empty() { + record.module_path().unwrap_or("unk") + } else { + record.target() + }, + record.line().unwrap_or(0), + ); + + const MAX_CHUNK_SIZE: usize = 3500; + + let message_chars = record.args().to_string().chars().collect::>(); + let chunks = message_chars.chunks(MAX_CHUNK_SIZE); + let chunks_len = chunks.len(); + for (index, chunk) in chunks.enumerate() { + unsafe { + let chunk = chunk.iter().cloned().collect::(); + if chunks_len == 1 { + obs::blog( + log_level.0, + format!("{message_prefix}: {chunk}\0").as_ptr() as *const i8, + ); + } else { + obs::blog( + log_level.0, + format!( + "{message_prefix}: MULTIPART [{}/{chunks_len}] {chunk}\0", + index + 1, + ) + .as_ptr() as *const i8, + ); + } + } + } + } + + fn flush(&self) {} +} diff --git a/rtc-backend/src/output.rs b/rtc-backend/src/output.rs new file mode 100644 index 0000000..2e7e774 --- /dev/null +++ b/rtc-backend/src/output.rs @@ -0,0 +1,443 @@ +use crate::whip; +use anyhow::{anyhow, Result}; +use bytes::Bytes; +use log::{debug, error, info, trace}; +use reqwest::Url; +use std::{ + boxed::Box, + sync::{Arc, Mutex, RwLock}, + thread::JoinHandle, + time::{Duration, Instant}, +}; +use tokio::{ + select, + sync::mpsc::{self, UnboundedReceiver, UnboundedSender}, + time::interval, +}; +use webrtc::{ + api::{ + interceptor_registry::register_default_interceptors, + media_engine::{MediaEngine, MIME_TYPE_H264, MIME_TYPE_OPUS}, + setting_engine::SettingEngine, + APIBuilder, + }, + ice_transport::ice_connection_state::RTCIceConnectionState, + interceptor::registry::Registry, + media::Sample, + peer_connection::{ + configuration::RTCConfiguration, peer_connection_state::RTCPeerConnectionState, + RTCPeerConnection, + }, + rtp_transceiver::{ + rtp_codec::{RTCRtpCodecCapability, RTCRtpCodecParameters, RTPCodecType}, + rtp_transceiver_direction::RTCRtpTransceiverDirection, + RTCPFeedback, RTCRtpTransceiverInit, + }, + stats::StatsReportType, + track::track_local::track_local_static_sample::TrackLocalStaticSample, +}; + +pub type ErrorCallback = Box; +#[derive(Debug)] +pub struct EncodedPacket { + pub data: Vec, + pub duration: Duration, + pub typ: EncodedPacketType, +} + +#[derive(Debug)] +pub enum EncodedPacketType { + Audio, + Video, +} + +/// Errors for [`OutputStream`] worker thread +#[derive(Debug)] +pub enum OutputStreamError { + ConnectFailed, + NetworkError, + WriteError(webrtc::Error), +} + +#[derive(Debug)] +pub enum Message { + Packet(EncodedPacket), + Error(OutputStreamError), + Close, +} + +pub enum WorkerResult { + Error(OutputStreamError), + Close, +} + +#[derive(Default)] +struct OutputStreamStats { + bytes_sent: u64, + congestion: f64, + dropped_frames: i32, + connect_time: Option, + connect_duration: Duration, +} + +pub struct OutputStream { + video_track: Arc, + audio_track: Arc, + peer_connection: Arc, + stats: Arc>, + worker_tx: UnboundedSender, + worker_handle: Arc>>>, + whip_resource: Arc>>, + error_callback: Arc>>, +} + +impl OutputStream { + fn start_worker(&mut self, mut worker_rx: UnboundedReceiver) { + let worker_handle = std::thread::spawn({ + let audio_track = self.audio_track.clone(); + let video_track = self.video_track.clone(); + let peer_connection = self.peer_connection.clone(); + let stats = self.stats.clone(); + let error_callback = self.error_callback.clone(); + move || { + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .thread_name("webrtc_worker") + .worker_threads(4) + .build() + .unwrap(); + let runtime = runtime; + let result = runtime.block_on( + async move { + let mut interval = interval(Duration::from_millis(500)); + 'worker_loop: loop { + let result = select! { + message = worker_rx.recv() => { + match message { + Some(Message::Packet(packet)) => { + let sample = Sample { + data: Bytes::from(packet.data), + duration: packet.duration, + ..Default::default() + }; + match (packet.typ, peer_connection.connection_state()) { + (_, RTCPeerConnectionState::Failed | RTCPeerConnectionState::Closed) => Err(WorkerResult::Error(OutputStreamError::NetworkError)), + (EncodedPacketType::Audio, _) => audio_track.write_sample(&sample).await.map_err(|e| WorkerResult::Error(OutputStreamError::WriteError(e))), + (EncodedPacketType::Video, _) => video_track.write_sample(&sample).await.map_err(|e| WorkerResult::Error(OutputStreamError::WriteError(e))) + } + }, + Some(Message::Error(e)) => Err(WorkerResult::Error(e)), + Some(Message::Close) | None => Err(WorkerResult::Close), + } + } + _ = interval.tick() => { + let pc_stats = peer_connection.get_stats().await; + + let stats = &mut stats.write().unwrap(); + if let Some(StatsReportType::Transport(transport_stats)) = pc_stats.reports.get("ice_transport") { + stats.bytes_sent = transport_stats.bytes_sent as u64; + } + Ok(()) + } + }; + + if let Err(e) = result { + break 'worker_loop e; + } + } + } + ); + + match result { + WorkerResult::Close => {} + WorkerResult::Error(e) => { + if let Some(callback) = &mut *error_callback.lock().unwrap() { + error!("Worker encountered error: {e:?}"); + callback(e); + } + } + } + + info!("Exiting worker thread"); + } + }); + + *self.worker_handle.lock().unwrap() = Some(worker_handle); + } + + pub async fn new() -> Result { + let video_track = Arc::new(TrackLocalStaticSample::new( + RTCRtpCodecCapability { + mime_type: MIME_TYPE_H264.to_owned(), + clock_rate: 90000, + sdp_fmtp_line: "profile-level-id=42e01f; max-fs=3600; max-mbps=108000; max-br=1400" + .to_string(), + ..Default::default() + }, + "video".to_owned(), + "webrtc-rs".to_owned(), + )); + + let audio_track = Arc::new(TrackLocalStaticSample::new( + RTCRtpCodecCapability { + mime_type: MIME_TYPE_OPUS.to_owned(), + ..Default::default() + }, + "audio".to_owned(), + "webrtc-rs".to_owned(), + )); + + let mut m = MediaEngine::default(); + m.register_codec( + RTCRtpCodecParameters { + capability: RTCRtpCodecCapability { + mime_type: MIME_TYPE_OPUS.to_owned(), + clock_rate: 48000, + channels: 2, + sdp_fmtp_line: "minptime=10;useinbandfec=1".to_owned(), + rtcp_feedback: vec![], + }, + payload_type: 111, + ..Default::default() + }, + RTPCodecType::Audio, + )?; + + m.register_codec( + RTCRtpCodecParameters { + capability: RTCRtpCodecCapability { + mime_type: MIME_TYPE_H264.to_owned(), + clock_rate: 90000, + channels: 0, + sdp_fmtp_line: + "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f" + .to_owned(), + rtcp_feedback: vec![ + RTCPFeedback { + typ: "goog-remb".to_owned(), + parameter: "".to_owned(), + }, + RTCPFeedback { + typ: "ccm".to_owned(), + parameter: "fir".to_owned(), + }, + RTCPFeedback { + typ: "nack".to_owned(), + parameter: "".to_owned(), + }, + RTCPFeedback { + typ: "nack".to_owned(), + parameter: "pli".to_owned(), + }, + ], + }, + payload_type: 125, + ..Default::default() + }, + RTPCodecType::Video, + )?; + + let mut registry = Registry::new(); + registry = register_default_interceptors(registry, &mut m)?; + + let gather_ips: Vec<_> = if_addrs::get_if_addrs() + .unwrap_or_else(|_| Vec::new()) + .into_iter() + .filter(|addr| { + if !addr.is_loopback() { + trace!( + "Valid gather address for interface {}: {:#?}", + addr.name, + addr.ip() + ); + true + } else { + trace!( + "Loopback address ignored for interface {}: {:#?}", + addr.name, + addr.ip() + ); + false + } + }) + .map(|addr| addr.ip()) + .collect(); + + let mut setting_engine = SettingEngine::default(); + + // Only attempt to limit if we found valid interfaces, otherwise do not attempt + // to limit as we may be on a platform that doesn't expose enough information + if !gather_ips.is_empty() { + debug!("Gather addresses: {gather_ips:#?}"); + setting_engine.set_ip_filter(Box::new(move |ip: std::net::IpAddr| -> bool { + gather_ips.contains(&ip) + })); + } + + let api = APIBuilder::new() + .with_media_engine(m) + .with_interceptor_registry(registry) + .with_setting_engine(setting_engine) + .build(); + + // Prepare the configuration + let config = RTCConfiguration { + ..Default::default() + }; + + let peer_connection = Arc::new(api.new_peer_connection(config).await?); + let stats: Arc> = Arc::new(RwLock::new(OutputStreamStats { + connect_time: None, + ..Default::default() + })); + + let (worker_tx, worker_rx): (UnboundedSender, UnboundedReceiver) = + mpsc::unbounded_channel(); + let error_callback: Arc>> = Arc::new(Mutex::new(None)); + + let mut output_stream = Self { + audio_track, + video_track, + peer_connection, + stats, + worker_tx, + worker_handle: Arc::new(Mutex::new(None)), + whip_resource: Arc::new(Mutex::new(None)), + error_callback, + }; + + output_stream.start_worker(worker_rx); + + Ok(output_stream) + } + + pub async fn connect(&self, url: &str, bearer_token: Option<&str>) { + self.connect_internal(url, bearer_token) + .await + .unwrap_or_else(|e| { + error!("Failed connecting to: {e}"); + let _ = self + .worker_tx + .send(Message::Error(OutputStreamError::ConnectFailed)); + }) + } + + async fn connect_internal(&self, url: &str, bearer_token: Option<&str>) -> Result<()> { + println!("Setting up webrtc!"); + + self.peer_connection + .add_transceiver_from_track( + self.video_track.clone(), + &[RTCRtpTransceiverInit { + direction: RTCRtpTransceiverDirection::Sendonly, + send_encodings: Vec::new(), + }], + ) + .await?; + + self.peer_connection + .add_transceiver_from_track( + self.audio_track.clone(), + &[RTCRtpTransceiverInit { + direction: RTCRtpTransceiverDirection::Sendonly, + send_encodings: Vec::new(), + }], + ) + .await?; + + self.peer_connection + .on_ice_connection_state_change(Box::new( + move |connection_state: RTCIceConnectionState| { + info!("Connection State has changed {}", connection_state); + if connection_state == RTCIceConnectionState::Connected { + // on_success(); + } + Box::pin(async {}) + }, + )); + + self.peer_connection + .on_peer_connection_state_change(Box::new(move |s: RTCPeerConnectionState| { + debug!("Peer Connection State has changed: {}", s); + + if s == RTCPeerConnectionState::Failed { + error!("Peer connection state went to failed") + } + + Box::pin(async {}) + })); + + let offer = self.peer_connection.create_offer(None).await?; + let mut gather_complete = self.peer_connection.gathering_complete_promise().await; + self.peer_connection.set_local_description(offer).await?; + + // Block until gathering complete + let _ = gather_complete.recv().await; + + let offer = self + .peer_connection + .local_description() + .await + .ok_or_else(|| anyhow!("No local description available"))?; + let (answer, whip_resource) = whip::offer(url, bearer_token, offer).await?; + self.peer_connection.set_remote_description(answer).await?; + + *self.whip_resource.lock().unwrap() = Some(whip_resource); + + Ok(()) + } + + pub async fn close(&self) -> Result<()> { + let close_result = self.worker_tx.send(Message::Close); + + // Take worker handle so it's dropped + let worker_future = self.worker_handle.lock().unwrap().take(); + // If close was a success (worker could receive messages), join on thread + // Otherwise, worker thread is already dead or currently closing due to error callback + if close_result.is_ok() { + if let Some(worker_future) = worker_future { + worker_future + .join() + .unwrap_or_else(|e| error!("Failed joining worker thread: {e:?}")); + } + } + + let whip_resource = self.whip_resource.lock().unwrap().take(); + if let Some(whip_resource) = whip_resource { + whip::delete(&whip_resource).await?; + } + Ok(self.peer_connection.close().await?) + } + + pub fn bytes_sent(&self) -> u64 { + self.stats.read().unwrap().bytes_sent + } + + pub fn congestion(&self) -> f64 { + self.stats.read().unwrap().congestion + } + + pub fn connect_time(&self) -> Duration { + let mut stats = self.stats.write().unwrap(); + + if let Some(connect_time) = stats.connect_time { + stats.connect_duration = Instant::now() - connect_time; + } + + stats.connect_duration + } + + pub fn dropped_frames(&self) -> i32 { + self.stats.read().unwrap().dropped_frames + } + + pub fn write(&self, packet: EncodedPacket) -> Result<()> { + self.worker_tx + .send(Message::Packet(packet)) + .map_err(|e| e.into()) + } + + pub fn set_error_callback(&self, callback: ErrorCallback) { + *self.error_callback.lock().unwrap() = Some(callback); + } +} diff --git a/rtc-backend/src/whip.rs b/rtc-backend/src/whip.rs new file mode 100644 index 0000000..5f062fb --- /dev/null +++ b/rtc-backend/src/whip.rs @@ -0,0 +1,93 @@ +use anyhow::Result; +use log::{info, warn}; +use reqwest::{ + header::{HeaderValue, AUTHORIZATION, CONTENT_TYPE, LOCATION, USER_AGENT}, + Url, +}; +use webrtc::peer_connection::sdp::session_description::RTCSessionDescription; + +use crate::obs_log; + +const OBS_VERSION: &str = env!("OBS_VERSION"); + +pub async fn offer( + url: &str, + bearer_token: Option<&str>, + local_desc: RTCSessionDescription, +) -> Result<(RTCSessionDescription, Url)> { + let client = reqwest::Client::new(); + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/sdp")); + headers.insert( + USER_AGENT, + HeaderValue::from_str(&format!("libobs/{OBS_VERSION}"))?, + ); + + if let Some(bearer_token) = bearer_token { + if !bearer_token.is_empty() { + headers.insert( + AUTHORIZATION, + HeaderValue::from_str(&format!("Bearer {bearer_token}"))?, + ); + } + } + + if obs_log::debug_whip() { + info!( + "[WHIP DEBUG | CAUTION SENSITIVE INFO] Sending offer to {url}: {}", + local_desc.sdp + ); + } + + let request = client.post(url).headers(headers).body(local_desc.sdp); + + if obs_log::debug_whip() { + info!("[WHIP DEBUG | CAUTION SENSITIVE INFO] Offer request {request:#?}"); + } + + let response = request.send().await?; + + if obs_log::debug_whip() { + info!("[WHIP DEBUG | CAUTION SENSITIVE INFO] Offer response: {response:#?}"); + } + + let mut url = response.url().to_owned(); + if let Some(location) = response.headers().get(LOCATION) { + url.set_path(location.to_str()?); + } + + let body = response.text().await?; + let sdp = RTCSessionDescription::answer(body)?; + + if obs_log::debug_whip() { + info!("[WHIP DEBUG | CAUTION SENSITIVE INFO] Answer SDP: {sdp:#?}"); + } + + Ok((sdp, url)) +} + +pub async fn delete(url: &Url) -> Result<()> { + let client = reqwest::Client::new(); + + let request = client.delete(url.to_owned()).header( + USER_AGENT, + HeaderValue::from_str(&format!("libobs/{OBS_VERSION}"))?, + ); + + if obs_log::debug_whip() { + info!("[WHIP DEBUG | CAUTION SENSITIVE INFO] Delete request {request:#?}"); + } + + let response = request.send().await?; + + if obs_log::debug_whip() { + info!("[WHIP DEBUG | CAUTION SENSITIVE INFO] Delete response {response:#?}"); + } + + if !response.status().is_success() { + warn!("Failed DELETE of whip resource: {}", response.status()) + } + + Ok(()) +} diff --git a/whip-output.c b/whip-output.c new file mode 100644 index 0000000..63cbf98 --- /dev/null +++ b/whip-output.c @@ -0,0 +1,232 @@ +#include "whip-output.h" + +static void whip_output_close_unsafe(struct whip_output *output) +{ + if (output && output->whip_output) { + char_rtc_output_close(output->whip_output); + char_rtc_output_free(output->whip_output); + output->whip_output = NULL; + } +} + +static const char *whip_output_getname(void *type_data) +{ + UNUSED_PARAMETER(type_data); + + return obs_module_text("Output.Name"); +} + +static void *whip_output_create(obs_data_t *settings, obs_output_t *obs_output) +{ + UNUSED_PARAMETER(settings); + + struct whip_output *output = bzalloc(sizeof(struct whip_output)); + pthread_mutex_init_value(&output->write_mutex); + + output->output = obs_output; + output->whip_output = NULL; + + // This needs to be recursive due to `obs_output_signal_stop` calling + // `whip_output_total_bytes_sent` (also guarded by mutex) + if (pthread_mutex_init_recursive(&output->write_mutex) != 0) + goto fail; + + return output; + +fail: + pthread_mutex_destroy(&output->write_mutex); + bfree(output); + return NULL; +} + +static void whip_output_destroy(void *data) +{ + struct whip_output *output = data; + + pthread_mutex_lock(&output->write_mutex); + whip_output_close_unsafe(output); + pthread_mutex_unlock(&output->write_mutex); + + pthread_mutex_destroy(&output->write_mutex); + bfree(output); +} + +static int map_rtc_error_to_obs_error(RTCOutputError error) +{ + switch (error) { + case RTCOutputError_ConnectFailed: + return OBS_OUTPUT_CONNECT_FAILED; + case RTCOutputError_NetworkError: + return OBS_OUTPUT_ERROR; + default: + blog(LOG_ERROR, "Invalid whip error code: %d", error); + return OBS_OUTPUT_ERROR; + } +} + +static void whip_output_error_callback(void *data, RTCOutputError error) +{ + struct whip_output *output = data; + pthread_mutex_lock(&output->write_mutex); + if (output->whip_output) { + whip_output_close_unsafe(output); + obs_output_signal_stop(output->output, + map_rtc_error_to_obs_error(error)); + } + pthread_mutex_unlock(&output->write_mutex); +} + +static bool whip_output_start(void *data) +{ + struct whip_output *output = data; + obs_service_t *service; + obs_data_t *service_settings; + const char *url, *bearer_token; + + service = obs_output_get_service(output->output); + if (!service) + return false; + + if (!obs_output_can_begin_data_capture(output->output, 0)) + return false; + + if (!obs_output_initialize_encoders(output->output, 0)) + return false; + + output->whip_output = char_rtc_output_new(); + if (!output->whip_output) { + blog(LOG_ERROR, "Unable to initialize whip output"); + return false; + } + + char_rtc_output_set_error_callback( + output->whip_output, + (RTCOutputErrorCallback)whip_output_error_callback, output); + + service_settings = obs_service_get_settings(service); + if (!service_settings) + return false; + + url = obs_service_get_url(service); + bearer_token = obs_data_get_string(service_settings, "bearer_token"); + char_rtc_output_connect(output->whip_output, url, bearer_token); + + obs_output_begin_data_capture(output->output, 0); + + obs_data_release(service_settings); + return true; +} + +static void whip_output_stop(void *data, uint64_t ts) +{ + UNUSED_PARAMETER(ts); + + struct whip_output *output = data; + + pthread_mutex_lock(&output->write_mutex); + whip_output_close_unsafe(output); + pthread_mutex_unlock(&output->write_mutex); + + obs_output_signal_stop(output->output, OBS_OUTPUT_SUCCESS); +} + +static void whip_output_data(void *data, struct encoder_packet *packet) +{ + struct whip_output *output = data; + int64_t duration = 0; + bool is_audio = false; + + if (packet->type == OBS_ENCODER_VIDEO) { + duration = packet->dts_usec - output->video_timestamp; + output->video_timestamp = packet->dts_usec; + } else if (packet->type == OBS_ENCODER_AUDIO) { + is_audio = true; + duration = packet->dts_usec - output->audio_timestamp; + output->audio_timestamp = packet->dts_usec; + } + + pthread_mutex_lock(&output->write_mutex); + if (output->whip_output) { + if (!char_rtc_output_write(output->whip_output, packet->data, + packet->size, duration, is_audio)) { + blog(LOG_ERROR, + "Unable to write packets to whip output"); + } + } + pthread_mutex_unlock(&output->write_mutex); +} + +static void whip_output_defaults(obs_data_t *defaults) +{ + UNUSED_PARAMETER(defaults); +} + +static obs_properties_t *whip_output_properties(void *unused) +{ + UNUSED_PARAMETER(unused); + + obs_properties_t *props = obs_properties_create(); + + return props; +} + +static uint64_t whip_output_total_bytes_sent(void *data) +{ + struct whip_output *output = data; + pthread_mutex_lock(&output->write_mutex); + uint64_t bytes_sent = 0; + if (output->whip_output) + bytes_sent = char_rtc_output_bytes_sent(output->whip_output); + pthread_mutex_unlock(&output->write_mutex); + + return bytes_sent; +} + +static int whip_output_dropped_frames(void *data) +{ + struct whip_output *output = data; + pthread_mutex_lock(&output->write_mutex); + uint32_t dropped_frames = 0; + if (output->whip_output) + dropped_frames = + char_rtc_output_dropped_frames(output->whip_output); + pthread_mutex_unlock(&output->write_mutex); + + return dropped_frames; +} + +static int whip_output_connect_time_ms(void *data) +{ + struct whip_output *output = data; + pthread_mutex_lock(&output->write_mutex); + uint32_t connect_time = 0; + if (output->whip_output) + connect_time = + char_rtc_output_connect_time_ms(output->whip_output); + pthread_mutex_unlock(&output->write_mutex); + + return connect_time; +} + +struct obs_output_info whip_output_info = { + .id = "whip_output", + .flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED | OBS_OUTPUT_SERVICE, + .encoded_video_codecs = "h264", + .encoded_audio_codecs = "opus", + .get_name = whip_output_getname, + .create = whip_output_create, + .destroy = whip_output_destroy, + .start = whip_output_start, + .stop = whip_output_stop, + .encoded_packet = whip_output_data, + .get_defaults = whip_output_defaults, + .get_properties = whip_output_properties, + .get_total_bytes = whip_output_total_bytes_sent, + .get_connect_time_ms = whip_output_connect_time_ms, + .get_dropped_frames = whip_output_dropped_frames, +}; + +void register_whip_output() +{ + obs_register_output(&whip_output_info); +} diff --git a/whip-output.h b/whip-output.h new file mode 100644 index 0000000..0d7d2f2 --- /dev/null +++ b/whip-output.h @@ -0,0 +1,17 @@ +#pragma once + +#include "obs-module.h" +#include "bindings.h" +#include + +struct whip_output { + obs_output_t *output; + + pthread_mutex_t write_mutex; + int64_t audio_timestamp; + int64_t video_timestamp; + + RTCOutput *whip_output; +}; + +void register_whip_output(void); diff --git a/whip-service.c b/whip-service.c new file mode 100644 index 0000000..2f24db6 --- /dev/null +++ b/whip-service.c @@ -0,0 +1,100 @@ +#include "whip-service.h" + +struct whip_service_state { + char *server; +}; + +static const char *whip_service_name(void *type_data) +{ + UNUSED_PARAMETER(type_data); + + return obs_module_text("Service.Name"); +} + +static void whip_service_update(void *data, obs_data_t *settings) +{ + struct whip_service_state *service = data; + + bfree(service->server); + + service->server = bstrdup(obs_data_get_string(settings, "server")); +} + +static void whip_service_destroy(void *data) +{ + struct whip_service_state *service = data; + + bfree(service->server); + bfree(service); +} + +static void *whip_service_create(obs_data_t *settings, obs_service_t *service) +{ + struct whip_service_state *data = + bzalloc(sizeof(struct whip_service_state)); + + whip_service_update(data, settings); + + UNUSED_PARAMETER(service); + return data; +} + +static const char *whip_service_url(void *data) +{ + struct whip_service_state *service = data; + return service->server; +} + +static obs_properties_t *whip_service_properties(void *data) +{ + UNUSED_PARAMETER(data); + + obs_properties_t *ppts = obs_properties_create(); + + obs_properties_add_text(ppts, "server", "URL", OBS_TEXT_DEFAULT); + obs_properties_add_text(ppts, "bearer_token", + obs_module_text("Service.BearerToken"), + OBS_TEXT_PASSWORD); + + return ppts; +} + +static const char *whip_service_get_output_type(void *data) +{ + UNUSED_PARAMETER(data); + + return "whip_output"; +} + +static void whip_service_apply_encoder_settings(void *data, + obs_data_t *video_settings, + obs_data_t *audio_settings) +{ + UNUSED_PARAMETER(data); + UNUSED_PARAMETER(audio_settings); + + // For now, ensure maximum compatibility with webrtc peers + if (video_settings) { + obs_data_set_int(video_settings, "bf", 0); + obs_data_set_string(video_settings, "profile", "baseline"); + obs_data_set_string(video_settings, "rate_control", "CBR"); + obs_data_set_bool(video_settings, "repeat_headers", true); + } +} + +struct obs_service_info whip_service_info = { + .id = "whip_custom", + .get_name = whip_service_name, + .create = whip_service_create, + .destroy = whip_service_destroy, + .update = whip_service_update, + .get_properties = whip_service_properties, + .get_url = whip_service_url, + .get_output_type = whip_service_get_output_type, + .apply_encoder_settings = whip_service_apply_encoder_settings, +}; + +void register_whip_service() +{ + obs_register_service(&whip_service_info); +} diff --git a/whip-service.h b/whip-service.h new file mode 100644 index 0000000..379d884 --- /dev/null +++ b/whip-service.h @@ -0,0 +1,5 @@ +#pragma once + +#include "obs-module.h" + +void register_whip_service(void);