| @@ -27,7 +27,7 @@ else | |||
| cargo test | |||
| pip install "maturin[zig]" | |||
| maturin build --zig | |||
| maturin build --release | |||
| # If GITHUB_EVENT_NAME is release or workflow_dispatch, publish the wheel on multiple platforms | |||
| if [ "$GITHUB_EVENT_NAME" == "release" ] || [ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]; then | |||
| # Free up ubuntu space | |||
| @@ -36,7 +36,7 @@ else | |||
| sudo rm -rf /usr/share/dotnet/ | |||
| sudo rm -rf /opt/ghc/ | |||
| maturin publish --skip-existing --zig | |||
| maturin publish --skip-existing | |||
| # aarch64-unknown-linux-gnu | |||
| rustup target add aarch64-unknown-linux-gnu | |||
| maturin publish --target aarch64-unknown-linux-gnu --skip-existing --zig | |||
| @@ -53,21 +53,20 @@ else | |||
| fi | |||
| elif [[ -f "Cargo.toml" && -f "pyproject.toml" && "$(uname)" = "Darwin" ]]; then | |||
| # x86_64-apple-darwin | |||
| pip install "maturin[zig]" | |||
| rustup target add x86_64-apple-darwin | |||
| maturin build --target x86_64-apple-darwin --zig --release | |||
| # aarch64-apple-darwin | |||
| maturin build --release | |||
| # If GITHUB_EVENT_NAME is release or workflow_dispatch, publish the wheel | |||
| if [ "$GITHUB_EVENT_NAME" == "release" ] || [ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]; then | |||
| maturin publish --target x86_64-apple-darwin --skip-existing --zig | |||
| maturin publish --skip-existing | |||
| fi | |||
| # aarch64-apple-darwin | |||
| rustup target add aarch64-apple-darwin | |||
| maturin build --target aarch64-apple-darwin --zig --release | |||
| # x86_64-apple-darwin | |||
| rustup target add x86_64-apple-darwin | |||
| maturin build --target x86_64-apple-darwin --zig --release | |||
| # If GITHUB_EVENT_NAME is release or workflow_dispatch, publish the wheel | |||
| if [ "$GITHUB_EVENT_NAME" == "release" ] || [ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]; then | |||
| maturin publish --target aarch64-apple-darwin --skip-existing --zig | |||
| maturin publish --target x86_64-apple-darwin --skip-existing --zig | |||
| fi | |||
| else | |||
| @@ -96,6 +95,7 @@ else | |||
| uv run pytest | |||
| fi | |||
| if [ "$GITHUB_EVENT_NAME" == "release" ] || [ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]; then | |||
| uv build | |||
| uv publish --check-url https://pypi.org/simple | |||
| fi | |||
| fi | |||
| @@ -1481,7 +1481,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "benchmark-example-node" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "dora-node-api", | |||
| "eyre", | |||
| @@ -1494,7 +1494,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "benchmark-example-sink" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "dora-node-api", | |||
| "eyre", | |||
| @@ -2267,7 +2267,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "communication-layer-pub-sub" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "flume 0.10.14", | |||
| "zenoh 0.7.0-rc", | |||
| @@ -2275,7 +2275,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "communication-layer-request-reply" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| [[package]] | |||
| name = "concurrent-queue" | |||
| @@ -3052,7 +3052,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-arrow-convert" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "arrow 54.2.1", | |||
| "chrono", | |||
| @@ -3063,7 +3063,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-cli" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "bat", | |||
| "clap 4.5.32", | |||
| @@ -3102,7 +3102,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-coordinator" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "ctrlc", | |||
| "dora-core", | |||
| @@ -3123,7 +3123,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-core" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "dora-message", | |||
| "eyre", | |||
| @@ -3142,7 +3142,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-daemon" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "aligned-vec", | |||
| "async-trait", | |||
| @@ -3183,12 +3183,13 @@ dependencies = [ | |||
| "dora-node-api", | |||
| "eyre", | |||
| "log", | |||
| "pyo3", | |||
| "structopt", | |||
| ] | |||
| [[package]] | |||
| name = "dora-download" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "eyre", | |||
| "reqwest 0.12.15", | |||
| @@ -3217,7 +3218,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-kit-car" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "dora-node-api", | |||
| "dotenv", | |||
| @@ -3252,7 +3253,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-metrics" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "eyre", | |||
| "opentelemetry 0.28.0", | |||
| @@ -3273,7 +3274,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-node-api" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "aligned-vec", | |||
| "arrow 54.2.1", | |||
| @@ -3298,7 +3299,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-node-api-c" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "arrow-array 54.2.1", | |||
| "dora-node-api", | |||
| @@ -3308,7 +3309,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-node-api-cxx" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "arrow 54.2.1", | |||
| "cxx", | |||
| @@ -3326,7 +3327,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-node-api-python" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "arrow 54.2.1", | |||
| "dora-daemon", | |||
| @@ -3346,7 +3347,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-object-to-pose" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "dora-node-api", | |||
| "eyre", | |||
| @@ -3355,7 +3356,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-openai-proxy-server" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "chrono", | |||
| "dora-node-api", | |||
| @@ -3376,7 +3377,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-operator-api" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "dora-arrow-convert", | |||
| "dora-operator-api-macros", | |||
| @@ -3385,14 +3386,14 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-operator-api-c" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "dora-operator-api-types", | |||
| ] | |||
| [[package]] | |||
| name = "dora-operator-api-cxx" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "cxx", | |||
| "cxx-build", | |||
| @@ -3401,7 +3402,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-operator-api-macros" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "proc-macro2", | |||
| "quote", | |||
| @@ -3410,7 +3411,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-operator-api-python" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "aligned-vec", | |||
| "arrow 54.2.1", | |||
| @@ -3426,7 +3427,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-operator-api-types" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "arrow 54.2.1", | |||
| "dora-arrow-convert", | |||
| @@ -3435,18 +3436,19 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-rav1e" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "bytemuck", | |||
| "dora-node-api", | |||
| "eyre", | |||
| "log", | |||
| "pyo3", | |||
| "rav1e", | |||
| ] | |||
| [[package]] | |||
| name = "dora-record" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "chrono", | |||
| "dora-node-api", | |||
| @@ -3458,7 +3460,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-rerun" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "bytemuck", | |||
| "chrono", | |||
| @@ -3473,7 +3475,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-ros2-bridge" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "array-init", | |||
| "dora-daemon", | |||
| @@ -3496,7 +3498,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-ros2-bridge-msg-gen" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "anyhow", | |||
| "glob", | |||
| @@ -3512,7 +3514,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-ros2-bridge-python" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "arrow 54.2.1", | |||
| "dora-ros2-bridge", | |||
| @@ -3526,7 +3528,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-runtime" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "aligned-vec", | |||
| "arrow 54.2.1", | |||
| @@ -3554,7 +3556,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "dora-tracing" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "eyre", | |||
| "opentelemetry 0.18.0", | |||
| @@ -7038,7 +7040,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "multiple-daemons-example-node" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "dora-node-api", | |||
| "eyre", | |||
| @@ -7049,14 +7051,14 @@ dependencies = [ | |||
| [[package]] | |||
| name = "multiple-daemons-example-operator" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "dora-operator-api", | |||
| ] | |||
| [[package]] | |||
| name = "multiple-daemons-example-sink" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "dora-node-api", | |||
| "eyre", | |||
| @@ -11004,7 +11006,7 @@ checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" | |||
| [[package]] | |||
| name = "receive_data" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "chrono", | |||
| "dora-node-api", | |||
| @@ -11473,7 +11475,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "rust-dataflow-example-node" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "dora-node-api", | |||
| "eyre", | |||
| @@ -11484,7 +11486,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "rust-dataflow-example-sink" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "dora-node-api", | |||
| "eyre", | |||
| @@ -11492,7 +11494,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "rust-dataflow-example-sink-dynamic" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "dora-node-api", | |||
| "eyre", | |||
| @@ -11500,7 +11502,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "rust-dataflow-example-status-node" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "dora-node-api", | |||
| "eyre", | |||
| @@ -11519,7 +11521,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "rust-ros2-dataflow-example-node" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "dora-node-api", | |||
| "dora-ros2-bridge", | |||
| @@ -12343,7 +12345,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "shared-memory-server" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "bincode", | |||
| "eyre", | |||
| @@ -13186,7 +13188,7 @@ dependencies = [ | |||
| [[package]] | |||
| name = "terminal-print" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| dependencies = [ | |||
| "dora-node-api", | |||
| "eyre", | |||
| @@ -49,33 +49,33 @@ members = [ | |||
| [workspace.package] | |||
| edition = "2021" | |||
| # Make sure to also bump `apis/node/python/__init__.py` version. | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| description = "`dora` goal is to be a low latency, composable, and distributed data flow." | |||
| documentation = "https://dora.carsmos.ai" | |||
| license = "Apache-2.0" | |||
| repository = "https://github.com/dora-rs/dora/" | |||
| [workspace.dependencies] | |||
| dora-node-api = { version = "0.3.10", path = "apis/rust/node", default-features = false } | |||
| dora-node-api-python = { version = "0.3.10", path = "apis/python/node", default-features = false } | |||
| dora-operator-api = { version = "0.3.10", path = "apis/rust/operator", default-features = false } | |||
| dora-operator-api-macros = { version = "0.3.10", path = "apis/rust/operator/macros" } | |||
| dora-operator-api-types = { version = "0.3.10", path = "apis/rust/operator/types" } | |||
| dora-operator-api-python = { version = "0.3.10", path = "apis/python/operator" } | |||
| dora-operator-api-c = { version = "0.3.10", path = "apis/c/operator" } | |||
| dora-node-api-c = { version = "0.3.10", path = "apis/c/node" } | |||
| dora-core = { version = "0.3.10", path = "libraries/core" } | |||
| dora-arrow-convert = { version = "0.3.10", path = "libraries/arrow-convert" } | |||
| dora-tracing = { version = "0.3.10", path = "libraries/extensions/telemetry/tracing" } | |||
| dora-metrics = { version = "0.3.10", path = "libraries/extensions/telemetry/metrics" } | |||
| dora-download = { version = "0.3.10", path = "libraries/extensions/download" } | |||
| shared-memory-server = { version = "0.3.10", path = "libraries/shared-memory-server" } | |||
| communication-layer-request-reply = { version = "0.3.10", path = "libraries/communication-layer/request-reply" } | |||
| dora-runtime = { version = "0.3.10", path = "binaries/runtime" } | |||
| dora-daemon = { version = "0.3.10", path = "binaries/daemon" } | |||
| dora-coordinator = { version = "0.3.10", path = "binaries/coordinator" } | |||
| dora-ros2-bridge = { version = "0.3.10", path = "libraries/extensions/ros2-bridge" } | |||
| dora-ros2-bridge-msg-gen = { version = "0.3.10", path = "libraries/extensions/ros2-bridge/msg-gen" } | |||
| dora-node-api = { version = "0.3.11", path = "apis/rust/node", default-features = false } | |||
| dora-node-api-python = { version = "0.3.11", path = "apis/python/node", default-features = false } | |||
| dora-operator-api = { version = "0.3.11", path = "apis/rust/operator", default-features = false } | |||
| dora-operator-api-macros = { version = "0.3.11", path = "apis/rust/operator/macros" } | |||
| dora-operator-api-types = { version = "0.3.11", path = "apis/rust/operator/types" } | |||
| dora-operator-api-python = { version = "0.3.11", path = "apis/python/operator" } | |||
| dora-operator-api-c = { version = "0.3.11", path = "apis/c/operator" } | |||
| dora-node-api-c = { version = "0.3.11", path = "apis/c/node" } | |||
| dora-core = { version = "0.3.11", path = "libraries/core" } | |||
| dora-arrow-convert = { version = "0.3.11", path = "libraries/arrow-convert" } | |||
| dora-tracing = { version = "0.3.11", path = "libraries/extensions/telemetry/tracing" } | |||
| dora-metrics = { version = "0.3.11", path = "libraries/extensions/telemetry/metrics" } | |||
| dora-download = { version = "0.3.11", path = "libraries/extensions/download" } | |||
| shared-memory-server = { version = "0.3.11", path = "libraries/shared-memory-server" } | |||
| communication-layer-request-reply = { version = "0.3.11", path = "libraries/communication-layer/request-reply" } | |||
| dora-runtime = { version = "0.3.11", path = "binaries/runtime" } | |||
| dora-daemon = { version = "0.3.11", path = "binaries/daemon" } | |||
| dora-coordinator = { version = "0.3.11", path = "binaries/coordinator" } | |||
| dora-ros2-bridge = { version = "0.3.11", path = "libraries/extensions/ros2-bridge" } | |||
| dora-ros2-bridge-msg-gen = { version = "0.3.11", path = "libraries/extensions/ros2-bridge/msg-gen" } | |||
| dora-ros2-bridge-python = { path = "libraries/extensions/ros2-bridge/python" } | |||
| # versioned independently from the other dora crates | |||
| dora-message = { version = "0.4.4", path = "libraries/message" } | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-argotranslate" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| description = "Dora Node for Text translating using Argostranslate" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| @@ -4,6 +4,10 @@ edition = "2021" | |||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
| [features] | |||
| default = [] | |||
| python = ["pyo3"] | |||
| [dependencies] | |||
| dav1d = "0.10" | |||
| bitstream-io = "2.0" | |||
| @@ -12,3 +16,15 @@ structopt = "0.3" | |||
| dora-node-api = { workspace = true, features = ["tracing"] } | |||
| eyre = "0.6.8" | |||
| bytemuck = "1.7.0" | |||
| pyo3 = { workspace = true, features = [ | |||
| "extension-module", | |||
| "abi3", | |||
| "eyre", | |||
| "generate-import-lib", | |||
| ], optional = true } | |||
| [lib] | |||
| name = "dora_dav1d" | |||
| path = "src/lib.rs" | |||
| crate-type = ["lib", "cdylib"] | |||
| @@ -0,0 +1,30 @@ | |||
| [build-system] | |||
| requires = ["maturin>=0.13.2"] | |||
| build-backend = "maturin" | |||
| [project] | |||
| name = "dora-dav1d" | |||
| dynamic = ["version"] | |||
| license = { text = "MIT" } | |||
| requires-python = ">=3.8" | |||
| dependencies = [ | |||
| "maturin>=1.8.2", | |||
| ] | |||
| scripts = { "dora-dav1d" = "dora_dav1d:py_main" } | |||
| [tool.maturin] | |||
| features = ["python", "pyo3/extension-module"] | |||
| [tool.ruff.lint] | |||
| extend-select = [ | |||
| "D", # pydocstyle | |||
| "UP", # Ruff's UP rule | |||
| "PERF", # Ruff's PERF rule | |||
| "RET", # Ruff's RET rule | |||
| "RSE", # Ruff's RSE rule | |||
| "NPY", # Ruff's NPY rule | |||
| "N", # Ruff's N rule | |||
| "I", # Ruff's I rule | |||
| ] | |||
| @@ -0,0 +1,129 @@ | |||
| use dav1d::Settings; | |||
| use dora_node_api::{arrow::array::UInt8Array, DoraNode, Event, IntoArrow}; | |||
| use eyre::{Context, Result}; | |||
| use log::warn; | |||
| fn yuv420_to_bgr( | |||
| y_plane: &[u8], | |||
| u_plane: &[u8], | |||
| v_plane: &[u8], | |||
| width: u32, | |||
| height: u32, | |||
| ) -> Vec<u8> { | |||
| let width = width as usize; | |||
| let height = height as usize; | |||
| let mut rgb_data = vec![0u8; width * height * 3]; // Output RGB data buffer | |||
| for j in 0..height { | |||
| for i in 0..width { | |||
| let y_idx = j * width + i; // Index in Y plane | |||
| let uv_idx = (j / 2) * (width / 2) + (i / 2); // Index in U/V planes | |||
| let y = y_plane[y_idx] as f32; | |||
| let u = u_plane[uv_idx] as f32 - 128.0; | |||
| let v = v_plane[uv_idx] as f32 - 128.0; | |||
| // Convert YUV to RGB using BT.601 standard formula | |||
| let r = (y + 1.402 * v).clamp(0.0, 255.0) as u8; | |||
| let g = (y - 0.344136 * u - 0.714136 * v).clamp(0.0, 255.0) as u8; | |||
| let b = (y + 1.772 * u).clamp(0.0, 255.0) as u8; | |||
| // Set the RGB values in the output buffer | |||
| let rgb_idx = y_idx * 3; | |||
| rgb_data[rgb_idx] = b; | |||
| rgb_data[rgb_idx + 1] = g; | |||
| rgb_data[rgb_idx + 2] = r; | |||
| } | |||
| } | |||
| rgb_data | |||
| } | |||
| pub fn lib_main() -> Result<()> { | |||
| let mut settings = Settings::new(); | |||
| // settings.set_n_threads(16); | |||
| settings.set_max_frame_delay(1); | |||
| let mut dec = | |||
| dav1d::Decoder::with_settings(&settings).expect("failed to create decoder instance"); | |||
| let (mut node, mut events) = | |||
| DoraNode::init_from_env().context("Could not initialize dora node")?; | |||
| loop { | |||
| match events.recv() { | |||
| Some(Event::Input { | |||
| id, | |||
| data, | |||
| mut metadata, | |||
| }) => { | |||
| if let Some(data) = data.as_any().downcast_ref::<UInt8Array>() { | |||
| let data = data.values().clone(); | |||
| match dec.send_data(data, None, None, None) { | |||
| Err(e) => { | |||
| warn!("Error sending data to the decoder: {}", e); | |||
| } | |||
| Ok(()) => { | |||
| if let Ok(p) = dec.get_picture() { | |||
| match p.pixel_layout() { | |||
| dav1d::PixelLayout::I420 => { | |||
| let y = p.plane(dav1d::PlanarImageComponent::Y); | |||
| let u = p.plane(dav1d::PlanarImageComponent::U); | |||
| let v = p.plane(dav1d::PlanarImageComponent::V); | |||
| let y = yuv420_to_bgr(&y, &u, &v, p.width(), p.height()); | |||
| let arrow = y.into_arrow(); | |||
| metadata.parameters.insert( | |||
| "encoding".to_string(), | |||
| dora_node_api::Parameter::String("bgr8".to_string()), | |||
| ); | |||
| node.send_output(id, metadata.parameters, arrow).unwrap(); | |||
| } | |||
| dav1d::PixelLayout::I400 => { | |||
| let y = p.plane(dav1d::PlanarImageComponent::Y); | |||
| let vec16: Vec<u16> = bytemuck::cast_slice(&y).to_vec(); | |||
| let arrow = vec16.into_arrow(); | |||
| metadata.parameters.insert( | |||
| "encoding".to_string(), | |||
| dora_node_api::Parameter::String("mono16".to_string()), | |||
| ); | |||
| node.send_output(id, metadata.parameters, arrow).unwrap(); | |||
| } | |||
| _ => { | |||
| warn!("Unsupported pixel layout"); | |||
| continue; | |||
| } | |||
| }; | |||
| } | |||
| } | |||
| } | |||
| } else { | |||
| warn!("Unsupported data type {}", data.data_type()); | |||
| continue; | |||
| } | |||
| } | |||
| None => break, | |||
| Some(_) => break, | |||
| } | |||
| } | |||
| Ok(()) | |||
| } | |||
| #[cfg(feature = "python")] | |||
| use pyo3::{ | |||
| pyfunction, pymodule, | |||
| types::{PyModule, PyModuleMethods}, | |||
| wrap_pyfunction, Bound, PyResult, Python, | |||
| }; | |||
| #[cfg(feature = "python")] | |||
| #[pyfunction] | |||
| fn py_main(_py: Python) -> eyre::Result<()> { | |||
| lib_main() | |||
| } | |||
| #[cfg(feature = "python")] | |||
| #[pymodule] | |||
| fn dora_kit_car(_py: Python, m: Bound<'_, PyModule>) -> PyResult<()> { | |||
| m.add_function(wrap_pyfunction!(py_main, &m)?)?; | |||
| m.add("__version__", env!("CARGO_PKG_VERSION"))?; | |||
| Ok(()) | |||
| } | |||
| @@ -1,108 +1,3 @@ | |||
| use dav1d::Settings; | |||
| use dora_node_api::{arrow::array::UInt8Array, DoraNode, Event, IntoArrow}; | |||
| use eyre::{Context, Result}; | |||
| use log::warn; | |||
| fn yuv420_to_bgr( | |||
| y_plane: &[u8], | |||
| u_plane: &[u8], | |||
| v_plane: &[u8], | |||
| width: u32, | |||
| height: u32, | |||
| ) -> Vec<u8> { | |||
| let width = width as usize; | |||
| let height = height as usize; | |||
| let mut rgb_data = vec![0u8; width * height * 3]; // Output RGB data buffer | |||
| for j in 0..height { | |||
| for i in 0..width { | |||
| let y_idx = j * width + i; // Index in Y plane | |||
| let uv_idx = (j / 2) * (width / 2) + (i / 2); // Index in U/V planes | |||
| let y = y_plane[y_idx] as f32; | |||
| let u = u_plane[uv_idx] as f32 - 128.0; | |||
| let v = v_plane[uv_idx] as f32 - 128.0; | |||
| // Convert YUV to RGB using BT.601 standard formula | |||
| let r = (y + 1.402 * v).clamp(0.0, 255.0) as u8; | |||
| let g = (y - 0.344136 * u - 0.714136 * v).clamp(0.0, 255.0) as u8; | |||
| let b = (y + 1.772 * u).clamp(0.0, 255.0) as u8; | |||
| // Set the RGB values in the output buffer | |||
| let rgb_idx = y_idx * 3; | |||
| rgb_data[rgb_idx] = b; | |||
| rgb_data[rgb_idx + 1] = g; | |||
| rgb_data[rgb_idx + 2] = r; | |||
| } | |||
| } | |||
| rgb_data | |||
| } | |||
| fn main() -> Result<()> { | |||
| let mut settings = Settings::new(); | |||
| // settings.set_n_threads(16); | |||
| settings.set_max_frame_delay(1); | |||
| let mut dec = | |||
| dav1d::Decoder::with_settings(&settings).expect("failed to create decoder instance"); | |||
| let (mut node, mut events) = | |||
| DoraNode::init_from_env().context("Could not initialize dora node")?; | |||
| loop { | |||
| match events.recv() { | |||
| Some(Event::Input { | |||
| id, | |||
| data, | |||
| mut metadata, | |||
| }) => { | |||
| if let Some(data) = data.as_any().downcast_ref::<UInt8Array>() { | |||
| let data = data.values().clone(); | |||
| match dec.send_data(data, None, None, None) { | |||
| Err(e) => { | |||
| warn!("Error sending data to the decoder: {}", e); | |||
| } | |||
| Ok(()) => { | |||
| if let Ok(p) = dec.get_picture() { | |||
| match p.pixel_layout() { | |||
| dav1d::PixelLayout::I420 => { | |||
| let y = p.plane(dav1d::PlanarImageComponent::Y); | |||
| let u = p.plane(dav1d::PlanarImageComponent::U); | |||
| let v = p.plane(dav1d::PlanarImageComponent::V); | |||
| let y = yuv420_to_bgr(&y, &u, &v, p.width(), p.height()); | |||
| let arrow = y.into_arrow(); | |||
| metadata.parameters.insert( | |||
| "encoding".to_string(), | |||
| dora_node_api::Parameter::String("bgr8".to_string()), | |||
| ); | |||
| node.send_output(id, metadata.parameters, arrow).unwrap(); | |||
| } | |||
| dav1d::PixelLayout::I400 => { | |||
| let y = p.plane(dav1d::PlanarImageComponent::Y); | |||
| let vec16: Vec<u16> = bytemuck::cast_slice(&y).to_vec(); | |||
| let arrow = vec16.into_arrow(); | |||
| metadata.parameters.insert( | |||
| "encoding".to_string(), | |||
| dora_node_api::Parameter::String("mono16".to_string()), | |||
| ); | |||
| node.send_output(id, metadata.parameters, arrow).unwrap(); | |||
| } | |||
| _ => { | |||
| warn!("Unsupported pixel layout"); | |||
| continue; | |||
| } | |||
| }; | |||
| } | |||
| } | |||
| } | |||
| } else { | |||
| warn!("Unsupported data type {}", data.data_type()); | |||
| continue; | |||
| } | |||
| } | |||
| None => break, | |||
| Some(_) => break, | |||
| } | |||
| } | |||
| Ok(()) | |||
| fn main() -> eyre::Result<()> { | |||
| dora_dav1d::lib_main() | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-distil-whisper" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-echo" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-internvl" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-ios-lidar" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [{ name = "Your Name", email = "email@email.com" }] | |||
| description = "dora-ios-lidar" | |||
| license = { text = "MIT" } | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-keyboard" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-kokoro-tts" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [{ name = "Your Name", email = "email@email.com" }] | |||
| description = "dora-kokoro-tts" | |||
| license = { text = "MIT" } | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-microphone" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, | |||
| @@ -1,6 +1,6 @@ | |||
| [package] | |||
| name = "dora-object-to-pose" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| edition = "2021" | |||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-openai-server" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-opus" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| description = "Dora Node for Text translating using Opus" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-outtetts" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [] | |||
| description = "dora-outtetts" | |||
| license = { text = "MIT" } | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-parler" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-piper" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [{ name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }] | |||
| description = "Dora Node for using Agilex piper" | |||
| license = { text = "MIT" } | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-pyaudio" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [{ name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }] | |||
| license = { text = "MIT" } | |||
| readme = "README.md" | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-pyorbbecksdk" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Xiang Yang", email = "Ryu-Yang@qq.com" }, | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-pyrealsense" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [{ name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }] | |||
| description = "Dora Node for capturing video with Pyrealsense" | |||
| license = { text = "MIT" } | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-qwen" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [{ name = "Your Name", email = "email@email.com" }] | |||
| description = "dora-qwen" | |||
| license = { text = "MIT" } | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-qwen2-5-vl" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-qwenvl" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, | |||
| @@ -9,9 +9,26 @@ repository.workspace = true | |||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
| [features] | |||
| default = [] | |||
| python = ["pyo3"] | |||
| [dependencies] | |||
| rav1e = { version = "0.7.1", features = ["serialize"] } | |||
| dora-node-api = { workspace = true, features = ["tracing"] } | |||
| eyre = "0.6.8" | |||
| log = "0.4" | |||
| bytemuck = "1.20" | |||
| pyo3 = { workspace = true, features = [ | |||
| "extension-module", | |||
| "abi3", | |||
| "eyre", | |||
| "generate-import-lib", | |||
| ], optional = true } | |||
| [lib] | |||
| name = "dora_rav1e" | |||
| path = "src/lib.rs" | |||
| crate-type = ["lib", "cdylib"] | |||
| @@ -0,0 +1,30 @@ | |||
| [build-system] | |||
| requires = ["maturin>=0.13.2"] | |||
| build-backend = "maturin" | |||
| [project] | |||
| name = "dora-rav1e" | |||
| dynamic = ["version"] | |||
| license = { text = "MIT" } | |||
| requires-python = ">=3.8" | |||
| dependencies = [ | |||
| "maturin>=1.8.2", | |||
| ] | |||
| scripts = { "dora-rav1e" = "dora_rav1e:py_main" } | |||
| [tool.maturin] | |||
| features = ["python", "pyo3/extension-module"] | |||
| [tool.ruff.lint] | |||
| extend-select = [ | |||
| "D", # pydocstyle | |||
| "UP", # Ruff's UP rule | |||
| "PERF", # Ruff's PERF rule | |||
| "RET", # Ruff's RET rule | |||
| "RSE", # Ruff's RSE rule | |||
| "NPY", # Ruff's NPY rule | |||
| "N", # Ruff's N rule | |||
| "I", # Ruff's I rule | |||
| ] | |||
| @@ -0,0 +1,371 @@ | |||
| // Copyright (c) 2019-2022, The rav1e contributors. All rights reserved | |||
| // | |||
| // This source code is subject to the terms of the BSD 2 Clause License and | |||
| // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License | |||
| // was not distributed with this source code in the LICENSE file, you can | |||
| // obtain it at www.aomedia.org/license/software. If the Alliance for Open | |||
| // Media Patent License 1.0 was not distributed with this source code in the | |||
| // PATENTS file, you can obtain it at www.aomedia.org/license/patent. | |||
| use std::env::var; | |||
| use dora_node_api::arrow::array::{UInt16Array, UInt8Array}; | |||
| use dora_node_api::{DoraNode, Event, IntoArrow, Parameter}; | |||
| use eyre::{Context as EyreContext, Result}; | |||
| use log::warn; | |||
| // Encode the same tiny blank frame 30 times | |||
| use rav1e::config::SpeedSettings; | |||
| use rav1e::*; | |||
| fn bgr8_to_yuv420(bgr_data: Vec<u8>, width: usize, height: usize) -> (Vec<u8>, Vec<u8>, Vec<u8>) { | |||
| let mut y_plane = vec![0; width * height]; | |||
| let mut u_plane = vec![0; (width / 2) * (height / 2)]; | |||
| let mut v_plane = vec![0; (width / 2) * (height / 2)]; | |||
| for y in 0..height { | |||
| for x in 0..width { | |||
| let bgr_index = (y * width + x) * 3; | |||
| let b = bgr_data[bgr_index] as f32; | |||
| let g = bgr_data[bgr_index + 1] as f32; | |||
| let r = bgr_data[bgr_index + 2] as f32; | |||
| // Corrected YUV conversion formulas | |||
| let y_value = (0.299 * r + 0.587 * g + 0.114 * b).clamp(0.0, 255.0); | |||
| let u_value = (-0.14713 * r - 0.28886 * g + 0.436 * b + 128.0).clamp(0.0, 255.0); | |||
| let v_value = (0.615 * r - 0.51499 * g - 0.10001 * b + 128.0).clamp(0.0, 255.0); | |||
| let y_index = y * width + x; | |||
| y_plane[y_index] = y_value.round() as u8; | |||
| if x % 2 == 0 && y % 2 == 0 { | |||
| let uv_index = (y / 2) * (width / 2) + (x / 2); | |||
| u_plane[uv_index] = u_value.round() as u8; | |||
| v_plane[uv_index] = v_value.round() as u8; | |||
| } | |||
| } | |||
| } | |||
| (y_plane, u_plane, v_plane) | |||
| } | |||
| fn get_yuv_planes(buffer: &[u8], width: usize, height: usize) -> (&[u8], &[u8], &[u8]) { | |||
| // Calculate sizes of Y, U, and V planes for YUV420 format | |||
| let y_size = width * height; // Y has full resolution | |||
| let uv_width = width / 2; // U and V are subsampled by 2 in both dimensions | |||
| let uv_height = height / 2; // U and V are subsampled by 2 in both dimensions | |||
| let uv_size = uv_width * uv_height; // Size for U and V planes | |||
| // Ensure the buffer has the correct size | |||
| // if buffer.len() != y_size + 2 * uv_size { | |||
| // panic!("Invalid buffer size for the given width and height!"); | |||
| // } | |||
| // Extract Y, U, and V planes | |||
| let y_plane = &buffer[0..y_size]; //.to_vec(); | |||
| let u_plane = &buffer[y_size..y_size + uv_size]; //.to_vec(); | |||
| let v_plane = &buffer[y_size + uv_size..]; //.to_vec(); | |||
| (y_plane, u_plane, v_plane) | |||
| } | |||
| pub fn lib_main() -> Result<()> { | |||
| let mut height = std::env::var("IMAGE_HEIGHT") | |||
| .unwrap_or_else(|_| "480".to_string()) | |||
| .parse() | |||
| .unwrap(); | |||
| let mut width = std::env::var("IMAGE_WIDTH") | |||
| .unwrap_or_else(|_| "640".to_string()) | |||
| .parse() | |||
| .unwrap(); | |||
| let speed = var("RAV1E_SPEED").map(|s| s.parse().unwrap()).unwrap_or(10); | |||
| let mut enc = EncoderConfig { | |||
| width, | |||
| height, | |||
| speed_settings: SpeedSettings::from_preset(speed), | |||
| low_latency: true, | |||
| ..Default::default() | |||
| }; | |||
| let cfg = Config::new().with_encoder_config(enc.clone()); | |||
| cfg.validate()?; | |||
| let (mut node, mut events) = | |||
| DoraNode::init_from_env().context("Could not initialize dora node")?; | |||
| loop { | |||
| match events.recv() { | |||
| Some(Event::Input { | |||
| id, | |||
| data, | |||
| mut metadata, | |||
| }) => { | |||
| if let Some(Parameter::Integer(h)) = metadata.parameters.get("height") { | |||
| height = *h as usize; | |||
| }; | |||
| if let Some(Parameter::Integer(w)) = metadata.parameters.get("width") { | |||
| width = *w as usize; | |||
| }; | |||
| let encoding = if let Some(Parameter::String(encoding)) = | |||
| metadata.parameters.get("encoding") | |||
| { | |||
| encoding | |||
| } else { | |||
| "bgr8" | |||
| }; | |||
| enc = EncoderConfig { | |||
| width, | |||
| height, | |||
| speed_settings: SpeedSettings::from_preset(speed), | |||
| low_latency: true, | |||
| ..Default::default() | |||
| }; | |||
| match encoding { | |||
| "mono16" => { | |||
| enc.bit_depth = 12; | |||
| enc.chroma_sampling = color::ChromaSampling::Cs400; | |||
| } | |||
| _ => {} | |||
| } | |||
| let cfg = Config::new().with_encoder_config(enc.clone()); | |||
| if encoding == "bgr8" { | |||
| let buffer: &UInt8Array = data.as_any().downcast_ref().unwrap(); | |||
| let buffer: Vec<u8> = buffer.values().to_vec(); | |||
| let (y, u, v) = bgr8_to_yuv420(buffer, width, height); | |||
| // Transpose values from BGR to RGB | |||
| // let buffer: Vec<u8> = buffer.chunks(3).flat_map(|x| [x[2], x[1], x[0]]).collect(); | |||
| let mut ctx: Context<u8> = cfg.new_context().unwrap(); | |||
| let mut f = ctx.new_frame(); | |||
| let xdec = f.planes[0].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[0].copy_from_raw_u8(&y, stride, 1); | |||
| let xdec = f.planes[1].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[1].copy_from_raw_u8(&u, stride, 1); | |||
| let xdec = f.planes[2].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[2].copy_from_raw_u8(&v, stride, 1); | |||
| match ctx.send_frame(f) { | |||
| Ok(_) => {} | |||
| Err(e) => match e { | |||
| EncoderStatus::EnoughData => { | |||
| warn!("Unable to send frame "); | |||
| continue; | |||
| } | |||
| _ => { | |||
| warn!("Unable to send frame "); | |||
| continue; | |||
| } | |||
| }, | |||
| } | |||
| metadata | |||
| .parameters | |||
| .insert("encoding".to_string(), Parameter::String("av1".to_string())); | |||
| ctx.flush(); | |||
| match ctx.receive_packet() { | |||
| Ok(pkt) => { | |||
| let data = pkt.data; | |||
| let arrow = data.into_arrow(); | |||
| node.send_output(id, metadata.parameters, arrow) | |||
| .context("could not send output") | |||
| .unwrap(); | |||
| } | |||
| Err(e) => match e { | |||
| EncoderStatus::LimitReached => {} | |||
| EncoderStatus::Encoded => {} | |||
| EncoderStatus::NeedMoreData => {} | |||
| _ => { | |||
| panic!("Unable to receive packet",); | |||
| } | |||
| }, | |||
| } | |||
| } else if encoding == "yuv420" { | |||
| let buffer: &UInt8Array = data.as_any().downcast_ref().unwrap(); | |||
| let buffer = buffer.values(); //.to_vec(); | |||
| let (y, u, v) = get_yuv_planes(buffer, width, height); | |||
| let mut ctx: Context<u8> = cfg.new_context().unwrap(); | |||
| let mut f = ctx.new_frame(); | |||
| let xdec = f.planes[0].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[0].copy_from_raw_u8(&y, stride, 1); | |||
| let xdec = f.planes[1].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[1].copy_from_raw_u8(&u, stride, 1); | |||
| let xdec = f.planes[2].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[2].copy_from_raw_u8(&v, stride, 1); | |||
| match ctx.send_frame(f) { | |||
| Ok(_) => {} | |||
| Err(e) => match e { | |||
| EncoderStatus::EnoughData => { | |||
| warn!("Unable to send frame "); | |||
| } | |||
| _ => { | |||
| warn!("Unable to send frame "); | |||
| } | |||
| }, | |||
| } | |||
| metadata | |||
| .parameters | |||
| .insert("encoding".to_string(), Parameter::String("av1".to_string())); | |||
| ctx.flush(); | |||
| match ctx.receive_packet() { | |||
| Ok(pkt) => { | |||
| let data = pkt.data; | |||
| let arrow = data.into_arrow(); | |||
| node.send_output(id, metadata.parameters, arrow) | |||
| .context("could not send output") | |||
| .unwrap(); | |||
| } | |||
| Err(e) => match e { | |||
| EncoderStatus::LimitReached => {} | |||
| EncoderStatus::Encoded => {} | |||
| EncoderStatus::NeedMoreData => {} | |||
| _ => { | |||
| panic!("Unable to receive packet",); | |||
| } | |||
| }, | |||
| } | |||
| } else if encoding == "mono16" { | |||
| let buffer: &UInt16Array = data.as_any().downcast_ref().unwrap(); | |||
| let buffer: &[u16] = buffer.values(); | |||
| // let buffer = shift_u16_slice_to_upper_12_bits(buffer); | |||
| let bytes: &[u8] = &bytemuck::cast_slice(&buffer); | |||
| let mut ctx: Context<u16> = cfg.new_context().unwrap(); | |||
| let mut f = ctx.new_frame(); | |||
| let xdec = f.planes[0].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| // Multiply by 2 the stride as it is going to be width * 2 as we're converting 16-bit to 2*8-bit. | |||
| f.planes[0].copy_from_raw_u8(bytes, stride * 2, 2); | |||
| match ctx.send_frame(f) { | |||
| Ok(_) => {} | |||
| Err(e) => match e { | |||
| EncoderStatus::EnoughData => { | |||
| warn!("Unable to send frame "); | |||
| } | |||
| _ => { | |||
| warn!("Unable to send frame "); | |||
| } | |||
| }, | |||
| } | |||
| metadata | |||
| .parameters | |||
| .insert("encoding".to_string(), Parameter::String("av1".to_string())); | |||
| ctx.flush(); | |||
| match ctx.receive_packet() { | |||
| Ok(pkt) => { | |||
| let data = pkt.data; | |||
| let arrow = data.into_arrow(); | |||
| node.send_output(id, metadata.parameters, arrow) | |||
| .context("could not send output") | |||
| .unwrap(); | |||
| } | |||
| Err(e) => match e { | |||
| EncoderStatus::LimitReached => {} | |||
| EncoderStatus::Encoded => {} | |||
| EncoderStatus::NeedMoreData => {} | |||
| _ => { | |||
| panic!("Unable to receive packet",); | |||
| } | |||
| }, | |||
| } | |||
| } else if encoding == "rgb8" { | |||
| let buffer: &UInt8Array = data.as_any().downcast_ref().unwrap(); | |||
| let buffer: Vec<u8> = buffer.values().to_vec(); | |||
| let buffer: Vec<u8> = | |||
| buffer.chunks(3).flat_map(|x| [x[2], x[1], x[0]]).collect(); | |||
| let (y, u, v) = bgr8_to_yuv420(buffer, width, height); | |||
| // Transpose values from BGR to RGB | |||
| let mut ctx: Context<u8> = cfg.new_context().unwrap(); | |||
| let mut f = ctx.new_frame(); | |||
| let xdec = f.planes[0].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[0].copy_from_raw_u8(&y, stride, 1); | |||
| let xdec = f.planes[1].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[1].copy_from_raw_u8(&u, stride, 1); | |||
| let xdec = f.planes[2].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[2].copy_from_raw_u8(&v, stride, 1); | |||
| match ctx.send_frame(f) { | |||
| Ok(_) => {} | |||
| Err(e) => match e { | |||
| EncoderStatus::EnoughData => { | |||
| warn!("Unable to send frame "); | |||
| continue; | |||
| } | |||
| _ => { | |||
| warn!("Unable to send frame "); | |||
| continue; | |||
| } | |||
| }, | |||
| } | |||
| metadata | |||
| .parameters | |||
| .insert("encoding".to_string(), Parameter::String("av1".to_string())); | |||
| ctx.flush(); | |||
| match ctx.receive_packet() { | |||
| Ok(pkt) => { | |||
| let data = pkt.data; | |||
| let arrow = data.into_arrow(); | |||
| node.send_output(id, metadata.parameters, arrow) | |||
| .context("could not send output") | |||
| .unwrap(); | |||
| } | |||
| Err(e) => match e { | |||
| EncoderStatus::LimitReached => {} | |||
| EncoderStatus::Encoded => {} | |||
| EncoderStatus::NeedMoreData => {} | |||
| _ => { | |||
| panic!("Unable to receive packet",); | |||
| } | |||
| }, | |||
| } | |||
| } else { | |||
| unimplemented!("We haven't worked on additional encodings."); | |||
| } | |||
| } | |||
| Some(Event::Error(_e)) => { | |||
| continue; | |||
| } | |||
| _ => break, | |||
| }; | |||
| } | |||
| Ok(()) | |||
| } | |||
| #[cfg(feature = "python")] | |||
| use pyo3::{ | |||
| pyfunction, pymodule, | |||
| types::{PyModule, PyModuleMethods}, | |||
| wrap_pyfunction, Bound, PyResult, Python, | |||
| }; | |||
| #[cfg(feature = "python")] | |||
| #[pyfunction] | |||
| fn py_main(_py: Python) -> eyre::Result<()> { | |||
| lib_main() | |||
| } | |||
| #[cfg(feature = "python")] | |||
| #[pymodule] | |||
| fn dora_kit_car(_py: Python, m: Bound<'_, PyModule>) -> PyResult<()> { | |||
| m.add_function(wrap_pyfunction!(py_main, &m)?)?; | |||
| m.add("__version__", env!("CARGO_PKG_VERSION"))?; | |||
| Ok(()) | |||
| } | |||
| @@ -1,350 +1,3 @@ | |||
| // Copyright (c) 2019-2022, The rav1e contributors. All rights reserved | |||
| // | |||
| // This source code is subject to the terms of the BSD 2 Clause License and | |||
| // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License | |||
| // was not distributed with this source code in the LICENSE file, you can | |||
| // obtain it at www.aomedia.org/license/software. If the Alliance for Open | |||
| // Media Patent License 1.0 was not distributed with this source code in the | |||
| // PATENTS file, you can obtain it at www.aomedia.org/license/patent. | |||
| use std::env::var; | |||
| use dora_node_api::arrow::array::{UInt16Array, UInt8Array}; | |||
| use dora_node_api::{DoraNode, Event, IntoArrow, Parameter}; | |||
| use eyre::{Context as EyreContext, Result}; | |||
| use log::warn; | |||
| // Encode the same tiny blank frame 30 times | |||
| use rav1e::config::SpeedSettings; | |||
| use rav1e::*; | |||
| fn bgr8_to_yuv420(bgr_data: Vec<u8>, width: usize, height: usize) -> (Vec<u8>, Vec<u8>, Vec<u8>) { | |||
| let mut y_plane = vec![0; width * height]; | |||
| let mut u_plane = vec![0; (width / 2) * (height / 2)]; | |||
| let mut v_plane = vec![0; (width / 2) * (height / 2)]; | |||
| for y in 0..height { | |||
| for x in 0..width { | |||
| let bgr_index = (y * width + x) * 3; | |||
| let b = bgr_data[bgr_index] as f32; | |||
| let g = bgr_data[bgr_index + 1] as f32; | |||
| let r = bgr_data[bgr_index + 2] as f32; | |||
| // Corrected YUV conversion formulas | |||
| let y_value = (0.299 * r + 0.587 * g + 0.114 * b).clamp(0.0, 255.0); | |||
| let u_value = (-0.14713 * r - 0.28886 * g + 0.436 * b + 128.0).clamp(0.0, 255.0); | |||
| let v_value = (0.615 * r - 0.51499 * g - 0.10001 * b + 128.0).clamp(0.0, 255.0); | |||
| let y_index = y * width + x; | |||
| y_plane[y_index] = y_value.round() as u8; | |||
| if x % 2 == 0 && y % 2 == 0 { | |||
| let uv_index = (y / 2) * (width / 2) + (x / 2); | |||
| u_plane[uv_index] = u_value.round() as u8; | |||
| v_plane[uv_index] = v_value.round() as u8; | |||
| } | |||
| } | |||
| } | |||
| (y_plane, u_plane, v_plane) | |||
| } | |||
| fn get_yuv_planes(buffer: &[u8], width: usize, height: usize) -> (&[u8], &[u8], &[u8]) { | |||
| // Calculate sizes of Y, U, and V planes for YUV420 format | |||
| let y_size = width * height; // Y has full resolution | |||
| let uv_width = width / 2; // U and V are subsampled by 2 in both dimensions | |||
| let uv_height = height / 2; // U and V are subsampled by 2 in both dimensions | |||
| let uv_size = uv_width * uv_height; // Size for U and V planes | |||
| // Ensure the buffer has the correct size | |||
| // if buffer.len() != y_size + 2 * uv_size { | |||
| // panic!("Invalid buffer size for the given width and height!"); | |||
| // } | |||
| // Extract Y, U, and V planes | |||
| let y_plane = &buffer[0..y_size]; //.to_vec(); | |||
| let u_plane = &buffer[y_size..y_size + uv_size]; //.to_vec(); | |||
| let v_plane = &buffer[y_size + uv_size..]; //.to_vec(); | |||
| (y_plane, u_plane, v_plane) | |||
| } | |||
| fn main() -> Result<()> { | |||
| let mut height = std::env::var("IMAGE_HEIGHT") | |||
| .unwrap_or_else(|_| "480".to_string()) | |||
| .parse() | |||
| .unwrap(); | |||
| let mut width = std::env::var("IMAGE_WIDTH") | |||
| .unwrap_or_else(|_| "640".to_string()) | |||
| .parse() | |||
| .unwrap(); | |||
| let speed = var("RAV1E_SPEED").map(|s| s.parse().unwrap()).unwrap_or(10); | |||
| let mut enc = EncoderConfig { | |||
| width, | |||
| height, | |||
| speed_settings: SpeedSettings::from_preset(speed), | |||
| low_latency: true, | |||
| ..Default::default() | |||
| }; | |||
| let cfg = Config::new().with_encoder_config(enc.clone()); | |||
| cfg.validate()?; | |||
| let (mut node, mut events) = | |||
| DoraNode::init_from_env().context("Could not initialize dora node")?; | |||
| loop { | |||
| match events.recv() { | |||
| Some(Event::Input { | |||
| id, | |||
| data, | |||
| mut metadata, | |||
| }) => { | |||
| if let Some(Parameter::Integer(h)) = metadata.parameters.get("height") { | |||
| height = *h as usize; | |||
| }; | |||
| if let Some(Parameter::Integer(w)) = metadata.parameters.get("width") { | |||
| width = *w as usize; | |||
| }; | |||
| let encoding = if let Some(Parameter::String(encoding)) = | |||
| metadata.parameters.get("encoding") | |||
| { | |||
| encoding | |||
| } else { | |||
| "bgr8" | |||
| }; | |||
| enc = EncoderConfig { | |||
| width, | |||
| height, | |||
| speed_settings: SpeedSettings::from_preset(speed), | |||
| low_latency: true, | |||
| ..Default::default() | |||
| }; | |||
| match encoding { | |||
| "mono16" => { | |||
| enc.bit_depth = 12; | |||
| enc.chroma_sampling = color::ChromaSampling::Cs400; | |||
| } | |||
| _ => {} | |||
| } | |||
| let cfg = Config::new().with_encoder_config(enc.clone()); | |||
| if encoding == "bgr8" { | |||
| let buffer: &UInt8Array = data.as_any().downcast_ref().unwrap(); | |||
| let buffer: Vec<u8> = buffer.values().to_vec(); | |||
| let (y, u, v) = bgr8_to_yuv420(buffer, width, height); | |||
| // Transpose values from BGR to RGB | |||
| // let buffer: Vec<u8> = buffer.chunks(3).flat_map(|x| [x[2], x[1], x[0]]).collect(); | |||
| let mut ctx: Context<u8> = cfg.new_context().unwrap(); | |||
| let mut f = ctx.new_frame(); | |||
| let xdec = f.planes[0].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[0].copy_from_raw_u8(&y, stride, 1); | |||
| let xdec = f.planes[1].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[1].copy_from_raw_u8(&u, stride, 1); | |||
| let xdec = f.planes[2].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[2].copy_from_raw_u8(&v, stride, 1); | |||
| match ctx.send_frame(f) { | |||
| Ok(_) => {} | |||
| Err(e) => match e { | |||
| EncoderStatus::EnoughData => { | |||
| warn!("Unable to send frame "); | |||
| continue; | |||
| } | |||
| _ => { | |||
| warn!("Unable to send frame "); | |||
| continue; | |||
| } | |||
| }, | |||
| } | |||
| metadata | |||
| .parameters | |||
| .insert("encoding".to_string(), Parameter::String("av1".to_string())); | |||
| ctx.flush(); | |||
| match ctx.receive_packet() { | |||
| Ok(pkt) => { | |||
| let data = pkt.data; | |||
| let arrow = data.into_arrow(); | |||
| node.send_output(id, metadata.parameters, arrow) | |||
| .context("could not send output") | |||
| .unwrap(); | |||
| } | |||
| Err(e) => match e { | |||
| EncoderStatus::LimitReached => {} | |||
| EncoderStatus::Encoded => {} | |||
| EncoderStatus::NeedMoreData => {} | |||
| _ => { | |||
| panic!("Unable to receive packet",); | |||
| } | |||
| }, | |||
| } | |||
| } else if encoding == "yuv420" { | |||
| let buffer: &UInt8Array = data.as_any().downcast_ref().unwrap(); | |||
| let buffer = buffer.values(); //.to_vec(); | |||
| let (y, u, v) = get_yuv_planes(buffer, width, height); | |||
| let mut ctx: Context<u8> = cfg.new_context().unwrap(); | |||
| let mut f = ctx.new_frame(); | |||
| let xdec = f.planes[0].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[0].copy_from_raw_u8(&y, stride, 1); | |||
| let xdec = f.planes[1].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[1].copy_from_raw_u8(&u, stride, 1); | |||
| let xdec = f.planes[2].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[2].copy_from_raw_u8(&v, stride, 1); | |||
| match ctx.send_frame(f) { | |||
| Ok(_) => {} | |||
| Err(e) => match e { | |||
| EncoderStatus::EnoughData => { | |||
| warn!("Unable to send frame "); | |||
| } | |||
| _ => { | |||
| warn!("Unable to send frame "); | |||
| } | |||
| }, | |||
| } | |||
| metadata | |||
| .parameters | |||
| .insert("encoding".to_string(), Parameter::String("av1".to_string())); | |||
| ctx.flush(); | |||
| match ctx.receive_packet() { | |||
| Ok(pkt) => { | |||
| let data = pkt.data; | |||
| let arrow = data.into_arrow(); | |||
| node.send_output(id, metadata.parameters, arrow) | |||
| .context("could not send output") | |||
| .unwrap(); | |||
| } | |||
| Err(e) => match e { | |||
| EncoderStatus::LimitReached => {} | |||
| EncoderStatus::Encoded => {} | |||
| EncoderStatus::NeedMoreData => {} | |||
| _ => { | |||
| panic!("Unable to receive packet",); | |||
| } | |||
| }, | |||
| } | |||
| } else if encoding == "mono16" { | |||
| let buffer: &UInt16Array = data.as_any().downcast_ref().unwrap(); | |||
| let buffer: &[u16] = buffer.values(); | |||
| // let buffer = shift_u16_slice_to_upper_12_bits(buffer); | |||
| let bytes: &[u8] = &bytemuck::cast_slice(&buffer); | |||
| let mut ctx: Context<u16> = cfg.new_context().unwrap(); | |||
| let mut f = ctx.new_frame(); | |||
| let xdec = f.planes[0].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| // Multiply by 2 the stride as it is going to be width * 2 as we're converting 16-bit to 2*8-bit. | |||
| f.planes[0].copy_from_raw_u8(bytes, stride * 2, 2); | |||
| match ctx.send_frame(f) { | |||
| Ok(_) => {} | |||
| Err(e) => match e { | |||
| EncoderStatus::EnoughData => { | |||
| warn!("Unable to send frame "); | |||
| } | |||
| _ => { | |||
| warn!("Unable to send frame "); | |||
| } | |||
| }, | |||
| } | |||
| metadata | |||
| .parameters | |||
| .insert("encoding".to_string(), Parameter::String("av1".to_string())); | |||
| ctx.flush(); | |||
| match ctx.receive_packet() { | |||
| Ok(pkt) => { | |||
| let data = pkt.data; | |||
| let arrow = data.into_arrow(); | |||
| node.send_output(id, metadata.parameters, arrow) | |||
| .context("could not send output") | |||
| .unwrap(); | |||
| } | |||
| Err(e) => match e { | |||
| EncoderStatus::LimitReached => {} | |||
| EncoderStatus::Encoded => {} | |||
| EncoderStatus::NeedMoreData => {} | |||
| _ => { | |||
| panic!("Unable to receive packet",); | |||
| } | |||
| }, | |||
| } | |||
| } else if encoding == "rgb8" { | |||
| let buffer: &UInt8Array = data.as_any().downcast_ref().unwrap(); | |||
| let buffer: Vec<u8> = buffer.values().to_vec(); | |||
| let buffer: Vec<u8> = | |||
| buffer.chunks(3).flat_map(|x| [x[2], x[1], x[0]]).collect(); | |||
| let (y, u, v) = bgr8_to_yuv420(buffer, width, height); | |||
| // Transpose values from BGR to RGB | |||
| let mut ctx: Context<u8> = cfg.new_context().unwrap(); | |||
| let mut f = ctx.new_frame(); | |||
| let xdec = f.planes[0].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[0].copy_from_raw_u8(&y, stride, 1); | |||
| let xdec = f.planes[1].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[1].copy_from_raw_u8(&u, stride, 1); | |||
| let xdec = f.planes[2].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[2].copy_from_raw_u8(&v, stride, 1); | |||
| match ctx.send_frame(f) { | |||
| Ok(_) => {} | |||
| Err(e) => match e { | |||
| EncoderStatus::EnoughData => { | |||
| warn!("Unable to send frame "); | |||
| continue; | |||
| } | |||
| _ => { | |||
| warn!("Unable to send frame "); | |||
| continue; | |||
| } | |||
| }, | |||
| } | |||
| metadata | |||
| .parameters | |||
| .insert("encoding".to_string(), Parameter::String("av1".to_string())); | |||
| ctx.flush(); | |||
| match ctx.receive_packet() { | |||
| Ok(pkt) => { | |||
| let data = pkt.data; | |||
| let arrow = data.into_arrow(); | |||
| node.send_output(id, metadata.parameters, arrow) | |||
| .context("could not send output") | |||
| .unwrap(); | |||
| } | |||
| Err(e) => match e { | |||
| EncoderStatus::LimitReached => {} | |||
| EncoderStatus::Encoded => {} | |||
| EncoderStatus::NeedMoreData => {} | |||
| _ => { | |||
| panic!("Unable to receive packet",); | |||
| } | |||
| }, | |||
| } | |||
| } else { | |||
| unimplemented!("We haven't worked on additional encodings."); | |||
| } | |||
| } | |||
| Some(Event::Error(_e)) => { | |||
| continue; | |||
| } | |||
| _ => break, | |||
| }; | |||
| } | |||
| Ok(()) | |||
| fn main() -> eyre::Result<()> { | |||
| dora_rav1e::lib_main() | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-rdt-1b" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [{ name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }] | |||
| description = "Dora Node for RDT 1B" | |||
| license = { text = "MIT" } | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-reachy2" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [{ name = "Your Name", email = "email@email.com" }] | |||
| description = "dora-reachy2" | |||
| license = { text = "MIT" } | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-sam2" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [{ name = "Your Name", email = "email@email.com" }] | |||
| description = "dora-sam2" | |||
| license = { text = "MIT" } | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-ugv" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [{ name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }] | |||
| description = "Dora Node for using Agilex UGV" | |||
| license = { text = "MIT" } | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-vad" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| description = "Dora Node for Text translating using Argostranslate" | |||
| authors = [{ name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }] | |||
| license = { text = "MIT" } | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "dora-yolo" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "llama-factory-recorder" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "opencv-plot" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "opencv-video-capture" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "pyarrow-assert" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "pyarrow-sender" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, | |||
| @@ -1,6 +1,6 @@ | |||
| [project] | |||
| name = "terminal-input" | |||
| version = "0.3.10" | |||
| version = "0.3.11" | |||
| authors = [ | |||
| { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, | |||
| { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, | |||