mirror of
https://github.com/fluxerapp/fluxer.git
synced 2026-09-02 21:04:06 +03:00
test: remove infrastructure-dependent integration suites (#1573)
This commit is contained in:
@@ -79,8 +79,6 @@ RUN apt-get update \
|
||||
rpm \
|
||||
unzip \
|
||||
webp \
|
||||
xauth \
|
||||
xvfb \
|
||||
xz-utils \
|
||||
xdg-utils \
|
||||
zstd \
|
||||
@@ -99,8 +97,6 @@ RUN apt-get update \
|
||||
hyperfine \
|
||||
iproute2 \
|
||||
iputils-ping \
|
||||
kind \
|
||||
kubernetes-client \
|
||||
lldb \
|
||||
lsof \
|
||||
ltrace \
|
||||
@@ -177,7 +173,6 @@ COPY --from=seaweedfs /usr/bin/weed /usr/local/bin/weed
|
||||
USER ${USERNAME}
|
||||
|
||||
ENV DOCKER_HOST="unix:///var/run/docker.sock" \
|
||||
KUBECONFIG="/workspaces/fluxer/.fluxer/k8s/local-kubeconfig" \
|
||||
PATH="/home/${USERNAME}/.cargo/bin:${PATH}" \
|
||||
PNPM_HOME="/home/${USERNAME}/.local/share/pnpm"
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
"shutdownAction": "stopCompose",
|
||||
"remoteUser": "vscode",
|
||||
"remoteEnv": {
|
||||
"DOCKER_HOST": "unix:///var/run/docker.sock",
|
||||
"KUBECONFIG": "/workspaces/fluxer/.fluxer/k8s/local-kubeconfig"
|
||||
"DOCKER_HOST": "unix:///var/run/docker.sock"
|
||||
},
|
||||
"runServices": ["workspace", "postgres", "valkey", "nats", "livekit", "meilisearch", "mailpit"],
|
||||
"forwardPorts": [
|
||||
|
||||
@@ -16,7 +16,6 @@ services:
|
||||
FLUXER_POSTGRES_HOST: postgres
|
||||
FLUXER_SELF_HOSTED: "true"
|
||||
DOCKER_HOST: unix:///var/run/docker.sock
|
||||
KUBECONFIG: /workspaces/fluxer/.fluxer/k8s/local-kubeconfig
|
||||
volumes:
|
||||
- ..:/workspaces/fluxer:cached
|
||||
- type: volume
|
||||
|
||||
@@ -75,8 +75,6 @@ else
|
||||
fi
|
||||
stage "rust: app proxy tests" cargo test -p fluxer_app_proxy
|
||||
|
||||
stage "integration: font serving" cargo run -q -p fluxer-dev -- font-serving-it
|
||||
|
||||
echo
|
||||
echo "---------------------------------------------"
|
||||
echo "${PASSED} stages passed, ${#FAILURES[@]} failed"
|
||||
|
||||
@@ -27,11 +27,6 @@
|
||||
**/target/
|
||||
**/test-results.json
|
||||
/fluxer_docs/site/
|
||||
/tools/integration/integration-driver/Cargo.lock
|
||||
/tools/integration/integration-driver/target-*/
|
||||
/tools/integration/results/*.json
|
||||
/tools/integration/results/latest-summary.txt
|
||||
|
||||
/fluxer_app/.devserver-cache.json
|
||||
/fluxer_app/pkgs/libfluxcore/
|
||||
/fluxer_app/src/features/i18n/locales/*/messages.mjs
|
||||
|
||||
@@ -134,13 +134,6 @@ jobs:
|
||||
cargo run --locked --quiet --manifest-path tools/ci/Cargo.toml -- build-app-proxy
|
||||
--step upload_assets
|
||||
|
||||
- name: verify bundled faces are CORS-usable from the app origin
|
||||
run: >-
|
||||
deploy/scripts/verify-asset-cors.sh
|
||||
--endpoint https://fluxerstatic.com
|
||||
--origin https://fluxer.app
|
||||
--manifest app-dist-output/dist/assets-manifest.txt
|
||||
|
||||
build-arm64:
|
||||
name: build app-proxy (arm64)
|
||||
needs: meta
|
||||
|
||||
@@ -234,56 +234,6 @@ jobs:
|
||||
- name: Verify shipped fonts match the lockfile
|
||||
run: python3 tools/fonts/build_fonts.py --verify
|
||||
|
||||
font-serving:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
|
||||
with:
|
||||
node-version: '24'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache cargo
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target
|
||||
key: rust-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
|
||||
restore-keys: |
|
||||
rust-${{ runner.os }}-
|
||||
|
||||
- name: Install native dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
pkg-config \
|
||||
build-essential \
|
||||
libcurl4-openssl-dev \
|
||||
libvips-dev \
|
||||
libavfilter-dev \
|
||||
libheif-dev \
|
||||
libwebp-dev
|
||||
|
||||
- name: Install Node.js dependencies
|
||||
run: pnpm --filter fluxer_admin --filter fluxer_marketing install
|
||||
|
||||
- name: Run the font serving integration suite
|
||||
run: cargo run --locked -q -p fluxer-dev -- font-serving-it
|
||||
|
||||
ci-scripts:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 25
|
||||
|
||||
Generated
-38
@@ -1926,9 +1926,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"base64",
|
||||
"chrono",
|
||||
"clap",
|
||||
"futures-util",
|
||||
"hmac 0.13.0",
|
||||
"hyper 1.10.1",
|
||||
"hyper-util",
|
||||
@@ -1942,9 +1940,7 @@ dependencies = [
|
||||
"sha2 0.11.0",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"url",
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5549,22 +5545,6 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"rustls 0.23.40",
|
||||
"rustls-native-certs",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.4",
|
||||
"tungstenite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.18"
|
||||
@@ -5742,24 +5722,6 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"data-encoding",
|
||||
"http 1.4.2",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand 0.9.4",
|
||||
"rustls 0.23.40",
|
||||
"rustls-pki-types",
|
||||
"sha1 0.10.6",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "twox-hash"
|
||||
version = "2.1.2"
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ENDPOINT="${ASSET_CORS_ENDPOINT:-https://fluxerstatic.com}"
|
||||
MANIFEST="${ASSET_MANIFEST:-app-dist-output/dist/assets-manifest.txt}"
|
||||
ORIGIN="${ASSET_CORS_ORIGIN:-https://fluxer.app}"
|
||||
SAMPLES="${ASSET_CORS_SAMPLES:-5}"
|
||||
EXPLICIT_KEYS=()
|
||||
|
||||
usage() {
|
||||
sed -n '3,22p' "$0"
|
||||
exit "${1:-2}"
|
||||
}
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--endpoint)
|
||||
ENDPOINT="${2:?--endpoint needs a URL}"
|
||||
shift 2
|
||||
;;
|
||||
--manifest)
|
||||
MANIFEST="${2:?--manifest needs a path}"
|
||||
shift 2
|
||||
;;
|
||||
--origin)
|
||||
ORIGIN="${2:?--origin needs an origin}"
|
||||
shift 2
|
||||
;;
|
||||
--samples)
|
||||
SAMPLES="${2:?--samples needs a count}"
|
||||
shift 2
|
||||
;;
|
||||
--key)
|
||||
EXPLICIT_KEYS+=("${2:?--key needs an S3 key}")
|
||||
shift 2
|
||||
;;
|
||||
-h | --help) usage 0 ;;
|
||||
*)
|
||||
echo "verify-asset-cors: unknown argument '$1'" >&2
|
||||
usage 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
ENDPOINT="${ENDPOINT%/}"
|
||||
|
||||
keys=()
|
||||
if [ "${#EXPLICIT_KEYS[@]}" -gt 0 ]; then
|
||||
keys=("${EXPLICIT_KEYS[@]}")
|
||||
elif [ -f "$MANIFEST" ]; then
|
||||
while IFS= read -r key; do
|
||||
keys+=("$key")
|
||||
done < <(grep -E '\.woff2$' "$MANIFEST" | awk -v n="$SAMPLES" '
|
||||
{ lines[NR] = $0 }
|
||||
END {
|
||||
if (NR == 0) exit
|
||||
if (n > NR) n = NR
|
||||
for (i = 0; i < n; i++) print lines[int(i * NR / n) + 1]
|
||||
}')
|
||||
else
|
||||
cat >&2 <<-EOF
|
||||
verify-asset-cors: no asset manifest at '$MANIFEST' and no --key given.
|
||||
|
||||
The manifest is produced by \`build-app-proxy --step generate_asset_manifest\` and uploaded
|
||||
by the release workflow as the 'app-proxy-assets-manifest' artifact. Pass --manifest to point
|
||||
at a downloaded copy, or --key to check a single face by hand.
|
||||
EOF
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ "${#keys[@]}" -eq 0 ]; then
|
||||
echo "verify-asset-cors: no .woff2 keys to check (manifest '$MANIFEST' has none)" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo "verify-asset-cors: endpoint=$ENDPOINT origin=$ORIGIN keys=${#keys[@]}"
|
||||
|
||||
failures=0
|
||||
for key in "${keys[@]}"; do
|
||||
url="$ENDPOINT/$key"
|
||||
headers="$(curl --silent --show-error --location --max-time 20 \
|
||||
--header "Origin: $ORIGIN" --output /dev/null --dump-header - "$url" 2>&1 || true)"
|
||||
|
||||
status="$(printf '%s\n' "$headers" | awk '/^HTTP\//{code=$2} END{print code+0}')"
|
||||
header_value() {
|
||||
printf '%s\n' "$headers" |
|
||||
tr -d '\r' |
|
||||
awk -v want="$1" 'index(tolower($0), want ":") == 1 {sub(/^[^:]*:[ \t]*/, ""); v=$0} END{print v}'
|
||||
}
|
||||
acao="$(header_value 'access-control-allow-origin')"
|
||||
ctype="$(header_value 'content-type')"
|
||||
cache="$(header_value 'cache-control')"
|
||||
cdn_cache="$(header_value 'cdn-cache-control')"
|
||||
|
||||
problems=()
|
||||
[ "$status" = "200" ] || problems+=("status=$status (want 200)")
|
||||
if [ -z "$acao" ]; then
|
||||
problems+=("no Access-Control-Allow-Origin -- the browser will drop this face")
|
||||
elif [ "$acao" != "*" ] && [ "$acao" != "$ORIGIN" ]; then
|
||||
problems+=("Access-Control-Allow-Origin='$acao' (want '*' or '$ORIGIN')")
|
||||
fi
|
||||
case "$ctype" in
|
||||
font/woff2*) ;;
|
||||
*) problems+=("Content-Type='$ctype' (want font/woff2)") ;;
|
||||
esac
|
||||
caching_note=""
|
||||
case "$cache$cdn_cache" in
|
||||
*immutable*) ;;
|
||||
*) caching_note=" (note: no 'immutable' token; cache-control='$cache')" ;;
|
||||
esac
|
||||
|
||||
if [ "${#problems[@]}" -eq 0 ]; then
|
||||
echo " OK $key [$status $ctype acao=$acao]$caching_note"
|
||||
else
|
||||
failures=$((failures + 1))
|
||||
echo " FAIL $key"
|
||||
for problem in "${problems[@]}"; do
|
||||
echo " - $problem"
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$failures" -gt 0 ]; then
|
||||
cat >&2 <<-EOF
|
||||
|
||||
verify-asset-cors: $failures of ${#keys[@]} sampled faces are NOT usable from $ORIGIN.
|
||||
|
||||
Most likely cause: the bucket's CORS rule is scoped to the retired 'fonts/' prefix and does
|
||||
not cover 'assets/'. Every bundled face fails in production while JS and CSS keep working,
|
||||
and the CDN still answers 200, so no server-side alarm fires.
|
||||
|
||||
Fix the bucket rule so it allows GET on 'assets/*' from the app origin (or bucket-wide),
|
||||
then re-run this check.
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "verify-asset-cors: all ${#keys[@]} sampled faces are usable from $ORIGIN"
|
||||
@@ -4,22 +4,11 @@
|
||||
mod parity_support;
|
||||
|
||||
use parity_support::{
|
||||
PARITY_RUN_ENV, PROTECTED_ROUTES_ENV, PUBLIC_ROUTES_ENV, TEST_ACCESS_TOKEN, TEST_ADMIN_SECRET,
|
||||
TEST_ADMIN_USER_ID, api_fixtures, capture, env_flag, html_normalizer, reference_worktree,
|
||||
route_list_from_env, servers,
|
||||
TEST_ACCESS_TOKEN, TEST_ADMIN_SECRET, TEST_ADMIN_USER_ID, api_fixtures, capture,
|
||||
html_normalizer, rust_server,
|
||||
};
|
||||
use std::{error::Error, io};
|
||||
|
||||
const DEFAULT_PUBLIC_ROUTES: &[&str] = &["/_health", "/robots.txt", "/static/app.css", "/login"];
|
||||
const DEFAULT_PROTECTED_ROUTES: &[&str] = &[
|
||||
"/dashboard",
|
||||
"/users?q=Parity",
|
||||
"/guilds?q=Parity",
|
||||
"/guilds/1600000000000000001",
|
||||
"/reports",
|
||||
"/reports/1700000000000000001",
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn html_normalizer_canonicalizes_attribute_order_and_csrf_values() {
|
||||
let left = r#"<form><input value="aaaaaaaa" name="_csrf" type="hidden"><svg><line x1="1" x2="2"></line></svg><a class="b" href="/static/app.css?v=123" id="x">Open</a></form>"#;
|
||||
@@ -64,7 +53,7 @@ async fn rust_admin_fixture_routes_cover_default_protected_routes() -> Result<()
|
||||
let api_server = api_fixtures::ApiFixtureServer::start_default()
|
||||
.await
|
||||
.map_err(test_error)?;
|
||||
let rust_admin = servers::start_rust_admin(api_server.base_url())
|
||||
let rust_admin = rust_server::start(api_server.base_url())
|
||||
.await
|
||||
.map_err(test_error)?;
|
||||
let client = capture::capture_client().map_err(test_error)?;
|
||||
@@ -115,68 +104,6 @@ async fn rust_admin_fixture_routes_cover_default_protected_routes() -> Result<()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[ignore = "set FLUXER_ADMIN_PARITY_RUN=1 to create/use the TS worktree and run dual-server parity"]
|
||||
async fn dual_server_static_public_and_protected_routes() -> Result<(), Box<dyn Error>> {
|
||||
if !env_flag(PARITY_RUN_ENV) {
|
||||
eprintln!("skipping dual-server parity; set {PARITY_RUN_ENV}=1 to run it");
|
||||
return Ok(());
|
||||
}
|
||||
let repo_root = repo_root()?;
|
||||
let api_server = api_fixtures::ApiFixtureServer::start_default()
|
||||
.await
|
||||
.map_err(test_error)?;
|
||||
let worktree = reference_worktree::ensure_reference_worktree(&repo_root).map_err(test_error)?;
|
||||
reference_worktree::prepare_reference_package(&worktree).map_err(test_error)?;
|
||||
let ts_port = servers::reserve_local_port().map_err(test_error)?;
|
||||
let ts_admin = servers::start_ts_admin(&worktree, ts_port, api_server.base_url())
|
||||
.await
|
||||
.map_err(test_error)?;
|
||||
let rust_admin = servers::start_rust_admin(api_server.base_url())
|
||||
.await
|
||||
.map_err(test_error)?;
|
||||
let client = capture::capture_client().map_err(test_error)?;
|
||||
let public_routes = route_list_from_env(PUBLIC_ROUTES_ENV, DEFAULT_PUBLIC_ROUTES);
|
||||
for route in public_routes {
|
||||
capture::compare_route(
|
||||
&client,
|
||||
&route,
|
||||
ts_admin.base_url(),
|
||||
rust_admin.base_url(),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map_err(test_error)?;
|
||||
}
|
||||
let session = fluxer_admin::session::create_session(
|
||||
TEST_ADMIN_USER_ID,
|
||||
TEST_ACCESS_TOKEN,
|
||||
TEST_ADMIN_SECRET,
|
||||
);
|
||||
let session_cookie = format!("{}={session}", fluxer_admin::session::SESSION_COOKIE_NAME);
|
||||
let protected_routes = route_list_from_env(PROTECTED_ROUTES_ENV, DEFAULT_PROTECTED_ROUTES);
|
||||
for route in protected_routes {
|
||||
capture::compare_route(
|
||||
&client,
|
||||
&route,
|
||||
ts_admin.base_url(),
|
||||
rust_admin.base_url(),
|
||||
Some(&session_cookie),
|
||||
)
|
||||
.await
|
||||
.map_err(test_error)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn repo_root() -> Result<std::path::PathBuf, Box<dyn Error>> {
|
||||
let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
manifest_dir
|
||||
.parent()
|
||||
.map(std::path::Path::to_path_buf)
|
||||
.ok_or_else(|| test_error("fluxer_admin must have a repository parent".to_owned()))
|
||||
}
|
||||
|
||||
fn test_error(message: String) -> Box<dyn Error> {
|
||||
Box::new(io::Error::other(message))
|
||||
}
|
||||
|
||||
@@ -18,24 +18,6 @@ pub fn capture_client() -> Result<Client, String> {
|
||||
.map_err(|error| format!("failed to build capture client: {error}"))
|
||||
}
|
||||
|
||||
pub async fn compare_route(
|
||||
client: &Client,
|
||||
route: &str,
|
||||
ts_base_url: &str,
|
||||
rust_base_url: &str,
|
||||
cookie: Option<&str>,
|
||||
) -> Result<(), String> {
|
||||
let ts = fetch_route(client, ts_base_url, route, cookie).await?;
|
||||
let rust = fetch_route(client, rust_base_url, route, cookie).await?;
|
||||
if ts == rust {
|
||||
return Ok(());
|
||||
}
|
||||
Err(format!(
|
||||
"parity mismatch for {route}\nTS: {ts:#?}\nRust: {rust:#?}\nfirst body diff: {}",
|
||||
first_body_diff(&ts.body, &rust.body)
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn fetch_route(
|
||||
client: &Client,
|
||||
base_url: &str,
|
||||
@@ -73,25 +55,3 @@ pub async fn fetch_route(
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
fn first_body_diff(left: &str, right: &str) -> String {
|
||||
let left_chars = left.chars().collect::<Vec<_>>();
|
||||
let right_chars = right.chars().collect::<Vec<_>>();
|
||||
let max_len = left_chars.len().max(right_chars.len());
|
||||
for index in 0..max_len {
|
||||
if left_chars.get(index) != right_chars.get(index) {
|
||||
let left_preview = preview_from(&left_chars, index);
|
||||
let right_preview = preview_from(&right_chars, index);
|
||||
return format!("at char {index}: left `{left_preview}`, right `{right_preview}`");
|
||||
}
|
||||
}
|
||||
"bodies differ but no character diff was found".to_owned()
|
||||
}
|
||||
|
||||
fn preview_from(chars: &[char], start: usize) -> String {
|
||||
chars
|
||||
.iter()
|
||||
.skip(start.saturating_sub(20))
|
||||
.take(80)
|
||||
.collect::<String>()
|
||||
}
|
||||
|
||||
@@ -3,47 +3,8 @@
|
||||
pub mod api_fixtures;
|
||||
pub mod capture;
|
||||
pub mod html_normalizer;
|
||||
pub mod reference_worktree;
|
||||
pub mod servers;
|
||||
pub mod rust_server;
|
||||
|
||||
use std::env;
|
||||
|
||||
pub const TS_REFERENCE_COMMIT: &str = "4748f2f1e6589c325fca6391d6df3e3c3f6a0345^";
|
||||
pub const PARITY_RUN_ENV: &str = "FLUXER_ADMIN_PARITY_RUN";
|
||||
pub const PUBLIC_ROUTES_ENV: &str = "FLUXER_ADMIN_PARITY_PUBLIC_ROUTES";
|
||||
pub const PROTECTED_ROUTES_ENV: &str = "FLUXER_ADMIN_PARITY_PROTECTED_ROUTES";
|
||||
pub const TS_WORKTREE_ENV: &str = "FLUXER_ADMIN_PARITY_TS_WORKTREE";
|
||||
pub const TS_WORKTREE_ROOT_ENV: &str = "FLUXER_ADMIN_PARITY_WORKTREE_ROOT";
|
||||
pub const SKIP_TS_PREPARE_ENV: &str = "FLUXER_ADMIN_PARITY_SKIP_TS_PREPARE";
|
||||
pub const TEST_ADMIN_SECRET: &str = "test-admin-secret";
|
||||
pub const TEST_ADMIN_USER_ID: &str = "1130650140672000000";
|
||||
pub const TEST_ACCESS_TOKEN: &str = "parity-access-token";
|
||||
|
||||
pub fn env_flag(name: &str) -> bool {
|
||||
env::var(name)
|
||||
.map(|value| {
|
||||
matches!(
|
||||
value.trim().to_ascii_lowercase().as_str(),
|
||||
"1" | "true" | "yes" | "on"
|
||||
)
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn route_list_from_env(name: &str, default: &[&str]) -> Vec<String> {
|
||||
match env::var(name) {
|
||||
Ok(value) => value
|
||||
.split(',')
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(|value| {
|
||||
if value.starts_with('/') {
|
||||
value.to_owned()
|
||||
} else {
|
||||
format!("/{value}")
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
Err(_) => default.iter().map(|route| (*route).to_owned()).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use super::{
|
||||
SKIP_TS_PREPARE_ENV, TS_REFERENCE_COMMIT, TS_WORKTREE_ENV, TS_WORKTREE_ROOT_ENV, env_flag,
|
||||
};
|
||||
use std::{
|
||||
env, fs,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
pub fn ensure_reference_worktree(repo_root: &Path) -> Result<PathBuf, String> {
|
||||
let target_commit = git_stdout(repo_root, &["rev-parse", TS_REFERENCE_COMMIT])?;
|
||||
if let Ok(path) = env::var(TS_WORKTREE_ENV) {
|
||||
let path = PathBuf::from(path);
|
||||
validate_worktree(&path, &target_commit)?;
|
||||
return Ok(path);
|
||||
}
|
||||
let root = env::var(TS_WORKTREE_ROOT_ENV)
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| repo_root.join("target/parity"));
|
||||
fs::create_dir_all(&root)
|
||||
.map_err(|error| format!("failed to create {}: {error}", root.display()))?;
|
||||
let worktree = root.join("fluxer-admin-ts-ref-4748f2f1-parent");
|
||||
if !worktree.exists() {
|
||||
run_git(
|
||||
repo_root,
|
||||
&[
|
||||
"worktree",
|
||||
"add",
|
||||
"--detach",
|
||||
path_arg(&worktree).as_str(),
|
||||
TS_REFERENCE_COMMIT,
|
||||
],
|
||||
)?;
|
||||
}
|
||||
validate_worktree(&worktree, &target_commit)?;
|
||||
Ok(worktree)
|
||||
}
|
||||
|
||||
pub fn prepare_reference_package(worktree: &Path) -> Result<(), String> {
|
||||
if env_flag(SKIP_TS_PREPARE_ENV) {
|
||||
return Ok(());
|
||||
}
|
||||
run_command(
|
||||
Command::new("pnpm")
|
||||
.current_dir(worktree)
|
||||
.args(["install", "--frozen-lockfile"]),
|
||||
"pnpm install --frozen-lockfile",
|
||||
)?;
|
||||
run_command(
|
||||
Command::new("pnpm")
|
||||
.current_dir(worktree)
|
||||
.args(["--filter", "@fluxer/config", "generate"]),
|
||||
"pnpm --filter @fluxer/config generate",
|
||||
)?;
|
||||
run_command(
|
||||
Command::new("pnpm")
|
||||
.current_dir(worktree)
|
||||
.args(["--filter", "fluxer_admin", "build:css"]),
|
||||
"pnpm --filter fluxer_admin build:css",
|
||||
)
|
||||
}
|
||||
|
||||
fn validate_worktree(worktree: &Path, target_commit: &str) -> Result<(), String> {
|
||||
if !worktree.join("fluxer_admin/package.json").exists() {
|
||||
return Err(format!(
|
||||
"{} does not look like the TS reference worktree",
|
||||
worktree.display()
|
||||
));
|
||||
}
|
||||
let head = git_stdout(worktree, &["rev-parse", "HEAD"])?;
|
||||
if head != target_commit {
|
||||
return Err(format!(
|
||||
"{} is at {head}, expected {target_commit}",
|
||||
worktree.display()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_git(repo: &Path, args: &[&str]) -> Result<(), String> {
|
||||
run_command(Command::new("git").current_dir(repo).args(args), "git")
|
||||
}
|
||||
|
||||
fn git_stdout(repo: &Path, args: &[&str]) -> Result<String, String> {
|
||||
let output = Command::new("git")
|
||||
.current_dir(repo)
|
||||
.args(args)
|
||||
.output()
|
||||
.map_err(|error| format!("failed to run git {}: {error}", args.join(" ")))?;
|
||||
if !output.status.success() {
|
||||
return Err(format!(
|
||||
"git {} failed\nstdout:\n{}\nstderr:\n{}",
|
||||
args.join(" "),
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
));
|
||||
}
|
||||
Ok(String::from_utf8_lossy(&output.stdout).trim().to_owned())
|
||||
}
|
||||
|
||||
fn run_command(command: &mut Command, label: &str) -> Result<(), String> {
|
||||
let output = command
|
||||
.output()
|
||||
.map_err(|error| format!("failed to run {label}: {error}"))?;
|
||||
if output.status.success() {
|
||||
return Ok(());
|
||||
}
|
||||
Err(format!(
|
||||
"{label} failed with status {}\nstdout:\n{}\nstderr:\n{}",
|
||||
output.status,
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
))
|
||||
}
|
||||
|
||||
fn path_arg(path: &Path) -> String {
|
||||
path.to_string_lossy().into_owned()
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use super::TEST_ADMIN_SECRET;
|
||||
use fluxer_admin::{
|
||||
build_router,
|
||||
config::{AdminConfig, ProxyConfig, RuntimeEnv},
|
||||
};
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio::{net::TcpListener, task::JoinHandle, time::sleep};
|
||||
|
||||
pub struct RunningRustAdmin {
|
||||
base_url: String,
|
||||
handle: JoinHandle<()>,
|
||||
}
|
||||
|
||||
impl RunningRustAdmin {
|
||||
pub fn base_url(&self) -> &str {
|
||||
&self.base_url
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RunningRustAdmin {
|
||||
fn drop(&mut self) {
|
||||
self.handle.abort();
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start(api_endpoint: &str) -> Result<RunningRustAdmin, String> {
|
||||
let listener = TcpListener::bind("127.0.0.1:0")
|
||||
.await
|
||||
.map_err(|error| format!("failed to bind Rust admin server: {error}"))?;
|
||||
let port = listener
|
||||
.local_addr()
|
||||
.map_err(|error| format!("failed to read Rust admin address: {error}"))?
|
||||
.port();
|
||||
let base_url = format!("http://127.0.0.1:{port}");
|
||||
let config = admin_config(port, api_endpoint, &base_url);
|
||||
let router = build_router(config);
|
||||
let handle = tokio::spawn(async move {
|
||||
let _ = axum::serve(listener, router).await;
|
||||
});
|
||||
wait_for_health(&base_url).await?;
|
||||
Ok(RunningRustAdmin { base_url, handle })
|
||||
}
|
||||
|
||||
fn admin_config(port: u16, api_endpoint: &str, admin_endpoint: &str) -> AdminConfig {
|
||||
AdminConfig {
|
||||
env: RuntimeEnv::Test,
|
||||
host: "127.0.0.1".to_owned(),
|
||||
port,
|
||||
secret_key_base: TEST_ADMIN_SECRET.to_owned(),
|
||||
base_path: String::new(),
|
||||
api_endpoint: api_endpoint.to_owned(),
|
||||
media_endpoint: format!("{api_endpoint}/media"),
|
||||
static_cdn_endpoint: "https://static.example.test".to_owned(),
|
||||
admin_endpoint: admin_endpoint.to_owned(),
|
||||
web_app_endpoint: "http://127.0.0.1:8088".to_owned(),
|
||||
kv_url: "redis://127.0.0.1:6379/0".to_owned(),
|
||||
oauth_client_id: "1234567890123456789".to_owned(),
|
||||
oauth_client_secret: "test-admin-oauth-secret".to_owned(),
|
||||
oauth_redirect_uri: format!("{admin_endpoint}/oauth2_callback"),
|
||||
build_version: "parity".to_owned(),
|
||||
release_channel: "parity".to_owned(),
|
||||
self_hosted: false,
|
||||
proxy: ProxyConfig {
|
||||
trust_client_ip_header: false,
|
||||
client_ip_header_name: "x-forwarded-for".to_owned(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn wait_for_health(base_url: &str) -> Result<(), String> {
|
||||
let client = reqwest::Client::builder()
|
||||
.redirect(reqwest::redirect::Policy::none())
|
||||
.build()
|
||||
.map_err(|error| format!("failed to build health client: {error}"))?;
|
||||
let deadline = Instant::now() + Duration::from_secs(30);
|
||||
let url = format!("{}/_health", base_url.trim_end_matches('/'));
|
||||
let mut last_error = String::new();
|
||||
while Instant::now() < deadline {
|
||||
match client.get(&url).send().await {
|
||||
Ok(response) if response.status().is_success() => return Ok(()),
|
||||
Ok(response) => last_error = format!("health returned {}", response.status()),
|
||||
Err(error) => last_error = error.to_string(),
|
||||
}
|
||||
sleep(Duration::from_millis(200)).await;
|
||||
}
|
||||
Err(format!("timed out waiting for {url}: {last_error}"))
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use super::TEST_ADMIN_SECRET;
|
||||
use fluxer_admin::{
|
||||
build_router,
|
||||
config::{AdminConfig, ProxyConfig, RuntimeEnv},
|
||||
};
|
||||
use std::{
|
||||
net::TcpListener as StdTcpListener,
|
||||
path::Path,
|
||||
process::{Child, Command, Stdio},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tokio::{net::TcpListener, task::JoinHandle, time::sleep};
|
||||
|
||||
pub struct RunningRustAdmin {
|
||||
base_url: String,
|
||||
handle: JoinHandle<()>,
|
||||
}
|
||||
|
||||
pub struct RunningTsAdmin {
|
||||
base_url: String,
|
||||
child: Child,
|
||||
}
|
||||
|
||||
impl RunningRustAdmin {
|
||||
pub fn base_url(&self) -> &str {
|
||||
&self.base_url
|
||||
}
|
||||
}
|
||||
|
||||
impl RunningTsAdmin {
|
||||
pub fn base_url(&self) -> &str {
|
||||
&self.base_url
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RunningRustAdmin {
|
||||
fn drop(&mut self) {
|
||||
self.handle.abort();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RunningTsAdmin {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.child.kill();
|
||||
let _ = self.child.wait();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reserve_local_port() -> Result<u16, String> {
|
||||
let listener = StdTcpListener::bind("127.0.0.1:0")
|
||||
.map_err(|error| format!("failed to reserve local port: {error}"))?;
|
||||
listener
|
||||
.local_addr()
|
||||
.map(|addr| addr.port())
|
||||
.map_err(|error| format!("failed to read reserved local port: {error}"))
|
||||
}
|
||||
|
||||
pub async fn start_rust_admin(api_endpoint: &str) -> Result<RunningRustAdmin, String> {
|
||||
let listener = TcpListener::bind("127.0.0.1:0")
|
||||
.await
|
||||
.map_err(|error| format!("failed to bind Rust admin server: {error}"))?;
|
||||
let port = listener
|
||||
.local_addr()
|
||||
.map_err(|error| format!("failed to read Rust admin address: {error}"))?
|
||||
.port();
|
||||
let base_url = format!("http://127.0.0.1:{port}");
|
||||
let config = admin_config(port, api_endpoint, &base_url);
|
||||
let router = build_router(config);
|
||||
let handle = tokio::spawn(async move {
|
||||
let _ = axum::serve(listener, router).await;
|
||||
});
|
||||
wait_for_health(&base_url).await?;
|
||||
Ok(RunningRustAdmin { base_url, handle })
|
||||
}
|
||||
|
||||
pub async fn start_ts_admin(
|
||||
worktree: &Path,
|
||||
port: u16,
|
||||
api_endpoint: &str,
|
||||
) -> Result<RunningTsAdmin, String> {
|
||||
let base_url = format!("http://127.0.0.1:{port}");
|
||||
let mut command = Command::new("pnpm");
|
||||
command
|
||||
.current_dir(worktree)
|
||||
.args(["--filter", "fluxer_admin", "start"])
|
||||
.env("BUILD_VERSION", "parity")
|
||||
.env("RELEASE_CHANNEL", "parity");
|
||||
for (key, value) in ts_admin_env(port, api_endpoint, &base_url) {
|
||||
command.env(key, value);
|
||||
}
|
||||
let child = command
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
.map_err(|error| format!("failed to start TS admin server: {error}"))?;
|
||||
let mut server = RunningTsAdmin { base_url, child };
|
||||
if let Err(error) = wait_for_health(server.base_url()).await {
|
||||
let _ = server.child.kill();
|
||||
let _ = server.child.wait();
|
||||
return Err(error);
|
||||
}
|
||||
Ok(server)
|
||||
}
|
||||
|
||||
fn admin_config(port: u16, api_endpoint: &str, admin_endpoint: &str) -> AdminConfig {
|
||||
AdminConfig {
|
||||
env: RuntimeEnv::Test,
|
||||
host: "127.0.0.1".to_owned(),
|
||||
port,
|
||||
secret_key_base: TEST_ADMIN_SECRET.to_owned(),
|
||||
base_path: String::new(),
|
||||
api_endpoint: api_endpoint.to_owned(),
|
||||
media_endpoint: format!("{api_endpoint}/media"),
|
||||
static_cdn_endpoint: "https://static.example.test".to_owned(),
|
||||
admin_endpoint: admin_endpoint.to_owned(),
|
||||
web_app_endpoint: "http://127.0.0.1:8088".to_owned(),
|
||||
kv_url: "redis://127.0.0.1:6379/0".to_owned(),
|
||||
oauth_client_id: "1234567890123456789".to_owned(),
|
||||
oauth_client_secret: "test-admin-oauth-secret".to_owned(),
|
||||
oauth_redirect_uri: format!("{admin_endpoint}/oauth2_callback"),
|
||||
build_version: "parity".to_owned(),
|
||||
release_channel: "parity".to_owned(),
|
||||
self_hosted: false,
|
||||
proxy: ProxyConfig {
|
||||
trust_client_ip_header: false,
|
||||
client_ip_header_name: "x-forwarded-for".to_owned(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn ts_admin_env(
|
||||
port: u16,
|
||||
api_endpoint: &str,
|
||||
admin_endpoint: &str,
|
||||
) -> Vec<(&'static str, String)> {
|
||||
vec![
|
||||
("FLUXER_ENV", "test".to_owned()),
|
||||
("NODE_ENV", "production".to_owned()),
|
||||
("FLUXER_BASE_DOMAIN", "127.0.0.1".to_owned()),
|
||||
("FLUXER_PUBLIC_SCHEME", "http".to_owned()),
|
||||
("FLUXER_PUBLIC_PORT", port.to_string()),
|
||||
("FLUXER_API_ENDPOINT", api_endpoint.to_owned()),
|
||||
("FLUXER_ADMIN_ENDPOINT", admin_endpoint.to_owned()),
|
||||
("FLUXER_APP_ENDPOINT", "http://127.0.0.1:8088".to_owned()),
|
||||
("FLUXER_MEDIA_ENDPOINT", format!("{api_endpoint}/media")),
|
||||
(
|
||||
"FLUXER_STATIC_CDN_ENDPOINT",
|
||||
"https://static.example.test".to_owned(),
|
||||
),
|
||||
("FLUXER_CASSANDRA_HOSTS", "127.0.0.1".to_owned()),
|
||||
("FLUXER_CASSANDRA_KEYSPACE", "fluxer_test".to_owned()),
|
||||
("FLUXER_CASSANDRA_LOCAL_DC", "datacenter1".to_owned()),
|
||||
("FLUXER_CASSANDRA_USERNAME", "cassandra".to_owned()),
|
||||
("FLUXER_CASSANDRA_PASSWORD", "cassandra".to_owned()),
|
||||
("FLUXER_KV_URL", "redis://127.0.0.1:6379/0".to_owned()),
|
||||
("FLUXER_S3_ACCESS_KEY_ID", "test".to_owned()),
|
||||
("FLUXER_S3_SECRET_ACCESS_KEY", "test".to_owned()),
|
||||
(
|
||||
"FLUXER_MEDIA_PROXY_SECRET_KEY",
|
||||
"test-media-secret".to_owned(),
|
||||
),
|
||||
("FLUXER_ADMIN_PORT", port.to_string()),
|
||||
("FLUXER_ADMIN_SECRET_KEY_BASE", TEST_ADMIN_SECRET.to_owned()),
|
||||
(
|
||||
"FLUXER_ADMIN_OAUTH_CLIENT_SECRET",
|
||||
"test-admin-oauth-secret".to_owned(),
|
||||
),
|
||||
(
|
||||
"FLUXER_MARKETING_SECRET_KEY_BASE",
|
||||
"test-marketing-secret".to_owned(),
|
||||
),
|
||||
("FLUXER_APP_PROXY_PORT", "8773".to_owned()),
|
||||
(
|
||||
"FLUXER_GATEWAY_MEDIA_PROXY_ENDPOINT",
|
||||
format!("{api_endpoint}/media"),
|
||||
),
|
||||
(
|
||||
"FLUXER_GATEWAY_RPC_AUTH_TOKEN",
|
||||
"test-gateway-rpc-token".to_owned(),
|
||||
),
|
||||
("FLUXER_GATEWAY_PUSH_ENABLED", "false".to_owned()),
|
||||
("FLUXER_SUDO_MODE_SECRET", "test-sudo-secret".to_owned()),
|
||||
(
|
||||
"FLUXER_CONNECTION_INITIATION_SECRET",
|
||||
"test-connection-secret".to_owned(),
|
||||
),
|
||||
(
|
||||
"FLUXER_VAPID_PUBLIC_KEY",
|
||||
"test-vapid-public-key".to_owned(),
|
||||
),
|
||||
(
|
||||
"FLUXER_VAPID_PRIVATE_KEY",
|
||||
"test-vapid-private-key".to_owned(),
|
||||
),
|
||||
("FLUXER_VAPID_EMAIL", "test@example.com".to_owned()),
|
||||
("FLUXER_EMAIL_ENABLED", "false".to_owned()),
|
||||
("FLUXER_LIVEKIT_ENABLED", "false".to_owned()),
|
||||
("FLUXER_STRIPE_ENABLED", "true".to_owned()),
|
||||
("FLUXER_SEARCH_URL", "http://127.0.0.1:9200".to_owned()),
|
||||
("FLUXER_SEARCH_API_KEY", "test".to_owned()),
|
||||
("FLUXER_CAPTCHA_ENABLED", "false".to_owned()),
|
||||
("FLUXER_CAPTCHA_PROVIDER", "none".to_owned()),
|
||||
("FLUXER_SELF_HOSTED", "false".to_owned()),
|
||||
("FLUXER_DISCOVERY_ENABLED", "true".to_owned()),
|
||||
("FLUXER_DISABLE_RATE_LIMITS", "true".to_owned()),
|
||||
("FLUXER_TEST_MODE_ENABLED", "true".to_owned()),
|
||||
]
|
||||
}
|
||||
|
||||
async fn wait_for_health(base_url: &str) -> Result<(), String> {
|
||||
let client = reqwest::Client::builder()
|
||||
.redirect(reqwest::redirect::Policy::none())
|
||||
.build()
|
||||
.map_err(|error| format!("failed to build health client: {error}"))?;
|
||||
let deadline = Instant::now() + Duration::from_secs(30);
|
||||
let url = format!("{}/_health", base_url.trim_end_matches('/'));
|
||||
let mut last_error = String::new();
|
||||
while Instant::now() < deadline {
|
||||
match client.get(&url).send().await {
|
||||
Ok(response) if response.status().is_success() => return Ok(()),
|
||||
Ok(response) => {
|
||||
last_error = format!("health returned {}", response.status());
|
||||
}
|
||||
Err(error) => {
|
||||
last_error = error.to_string();
|
||||
}
|
||||
}
|
||||
sleep(Duration::from_millis(200)).await;
|
||||
}
|
||||
Err(format!("timed out waiting for {url}: {last_error}"))
|
||||
}
|
||||
@@ -1,249 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {randomUUID} from 'node:crypto';
|
||||
import {existsSync} from 'node:fs';
|
||||
import {getDefaultPostgresClient, initPostgres, quoteIdentifier, shutdownPostgres} from '@pkgs/postgres/src/Client';
|
||||
import cassandra from 'cassandra-driver';
|
||||
import {afterAll, beforeAll, describe, expect, test} from 'vitest';
|
||||
import {defineTable} from './CassandraTableDsl';
|
||||
import {Db} from './CassandraTypes';
|
||||
import {ensurePostgresKvSchema, PostgresKvQueryExecutor, pruneExpiredPostgresKvRows} from './PostgresKvQueryExecutor';
|
||||
|
||||
interface TestRow {
|
||||
tenant_id: string;
|
||||
item_id: bigint;
|
||||
created_at: Date;
|
||||
payload: Buffer | null;
|
||||
tags: Set<string> | null;
|
||||
counts: Map<string, bigint> | null;
|
||||
local_day: cassandra.types.LocalDate | null;
|
||||
note: string | null;
|
||||
}
|
||||
|
||||
function postgresHost(): string {
|
||||
return process.env.FLUXER_POSTGRES_TEST_HOST ?? (existsSync('/.dockerenv') ? 'host.docker.internal' : '127.0.0.1');
|
||||
}
|
||||
|
||||
function sleep(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
const kvTable = `fluxer_kv_test_${randomUUID().replaceAll('-', '_')}`;
|
||||
const logicalTableName = `pg_kv_test_${randomUUID().replaceAll('-', '_')}`;
|
||||
const TestRows = defineTable<TestRow, 'tenant_id' | 'item_id', 'tenant_id'>({
|
||||
name: logicalTableName,
|
||||
columns: ['tenant_id', 'item_id', 'created_at', 'payload', 'tags', 'counts', 'local_day', 'note'],
|
||||
primaryKey: ['tenant_id', 'item_id'],
|
||||
partitionKey: ['tenant_id'],
|
||||
});
|
||||
|
||||
describe('PostgresKvQueryExecutor', () => {
|
||||
let executor: PostgresKvQueryExecutor;
|
||||
let initialized = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
await initPostgres({
|
||||
host: postgresHost(),
|
||||
port: Number(process.env.FLUXER_POSTGRES_PORT ?? 5432),
|
||||
database: process.env.FLUXER_POSTGRES_DATABASE ?? 'fluxer',
|
||||
username: process.env.FLUXER_POSTGRES_USERNAME ?? 'fluxer',
|
||||
password: process.env.FLUXER_POSTGRES_PASSWORD ?? 'fluxer',
|
||||
kvTable,
|
||||
maxConnections: 4,
|
||||
});
|
||||
initialized = true;
|
||||
const client = getDefaultPostgresClient();
|
||||
await ensurePostgresKvSchema(client);
|
||||
executor = new PostgresKvQueryExecutor(client);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (!initialized) return;
|
||||
try {
|
||||
await getDefaultPostgresClient().query(`DROP TABLE IF EXISTS ${quoteIdentifier(kvTable)}`);
|
||||
} finally {
|
||||
await shutdownPostgres();
|
||||
}
|
||||
});
|
||||
|
||||
test('round trips typed values and conditional writes through a real Postgres KV table', async () => {
|
||||
const row: TestRow = {
|
||||
tenant_id: 'tenant-a',
|
||||
item_id: 10n,
|
||||
created_at: new Date('2026-01-02T03:04:05.006Z'),
|
||||
payload: Buffer.from('hello postgres kv'),
|
||||
tags: new Set(['alpha', 'beta']),
|
||||
counts: new Map([['seen', 5n]]),
|
||||
local_day: cassandra.types.LocalDate.fromString('2026-01-02'),
|
||||
note: 'first',
|
||||
};
|
||||
|
||||
await expect(executor.executeQuery<{['[applied]']: boolean}>(TestRows.insertIfNotExists(row))).resolves.toEqual([
|
||||
{'[applied]': true},
|
||||
]);
|
||||
await expect(
|
||||
executor.executeQuery<{['[applied]']: boolean}>(TestRows.insertIfNotExists({...row, note: 'second'})),
|
||||
).resolves.toEqual([{'[applied]': false}]);
|
||||
|
||||
const fetched = await executor.executeQuery<TestRow>(
|
||||
TestRows.select({
|
||||
where: [TestRows.where.eq('tenant_id'), TestRows.where.eq('item_id')],
|
||||
limit: 1,
|
||||
}).bind({tenant_id: 'tenant-a', item_id: 10n}),
|
||||
);
|
||||
|
||||
expect(fetched).toHaveLength(1);
|
||||
expect(fetched[0]!.item_id).toBe(10n);
|
||||
expect(fetched[0]!.created_at.getTime()).toBe(row.created_at.getTime());
|
||||
expect(fetched[0]!.payload?.equals(row.payload!)).toBe(true);
|
||||
expect([...fetched[0]!.tags!.values()].sort()).toEqual(['alpha', 'beta']);
|
||||
expect(fetched[0]!.counts!.get('seen')).toBe(5n);
|
||||
expect(fetched[0]!.local_day?.toString()).toBe('2026-01-02');
|
||||
expect(fetched[0]!.note).toBe('first');
|
||||
});
|
||||
|
||||
test('filters, orders, patches, pages, and deletes logical rows', async () => {
|
||||
await executor.executeBatch(
|
||||
[
|
||||
TestRows.upsertAll({
|
||||
tenant_id: 'tenant-b',
|
||||
item_id: 1n,
|
||||
created_at: new Date('2026-02-01T00:00:00.000Z'),
|
||||
payload: null,
|
||||
tags: null,
|
||||
counts: null,
|
||||
local_day: null,
|
||||
note: 'one',
|
||||
}),
|
||||
TestRows.upsertAll({
|
||||
tenant_id: 'tenant-b',
|
||||
item_id: 2n,
|
||||
created_at: new Date('2026-02-02T00:00:00.000Z'),
|
||||
payload: null,
|
||||
tags: null,
|
||||
counts: null,
|
||||
local_day: null,
|
||||
note: 'two',
|
||||
}),
|
||||
TestRows.upsertAll({
|
||||
tenant_id: 'tenant-b',
|
||||
item_id: 3n,
|
||||
created_at: new Date('2026-02-03T00:00:00.000Z'),
|
||||
payload: null,
|
||||
tags: null,
|
||||
counts: null,
|
||||
local_day: null,
|
||||
note: 'three',
|
||||
}),
|
||||
].map((query) => ({query: query.cql, params: query.params, meta: query.kvMeta})),
|
||||
);
|
||||
|
||||
const firstPage = await executor.executePagedQuery<TestRow>(
|
||||
TestRows.select({
|
||||
where: TestRows.where.eq('tenant_id'),
|
||||
orderBy: {col: 'item_id', direction: 'DESC'},
|
||||
}).bind({tenant_id: 'tenant-b'}),
|
||||
{pageSize: 2},
|
||||
);
|
||||
expect(firstPage.rows.map((row) => row.item_id)).toEqual([3n, 2n]);
|
||||
expect(firstPage.pageState).not.toBeNull();
|
||||
|
||||
const secondPage = await executor.executePagedQuery<TestRow>(
|
||||
TestRows.select({
|
||||
where: TestRows.where.eq('tenant_id'),
|
||||
orderBy: {col: 'item_id', direction: 'DESC'},
|
||||
}).bind({tenant_id: 'tenant-b'}),
|
||||
{pageSize: 2, pageState: firstPage.pageState},
|
||||
);
|
||||
expect(secondPage.rows.map((row) => row.item_id)).toEqual([1n]);
|
||||
expect(secondPage.pageState).toBeNull();
|
||||
|
||||
await executor.executeQuery(TestRows.patchByPk({tenant_id: 'tenant-b', item_id: 2n}, {note: Db.clear()}));
|
||||
const patched = await executor.executeQuery<TestRow>(
|
||||
TestRows.select({where: [TestRows.where.eq('tenant_id'), TestRows.where.eq('item_id')], limit: 1}).bind({
|
||||
tenant_id: 'tenant-b',
|
||||
item_id: 2n,
|
||||
}),
|
||||
);
|
||||
expect(patched[0]!.note).toBeNull();
|
||||
|
||||
await executor.executeQuery(TestRows.delete({where: TestRows.where.eq('tenant_id')}).bind({tenant_id: 'tenant-b'}));
|
||||
const remaining = await executor.executeQuery<TestRow>(
|
||||
TestRows.select({where: TestRows.where.eq('tenant_id')}).bind({tenant_id: 'tenant-b'}),
|
||||
);
|
||||
expect(remaining).toEqual([]);
|
||||
});
|
||||
|
||||
test('filters expired rows and prunes them physically', async () => {
|
||||
const client = getDefaultPostgresClient();
|
||||
await executor.executeQuery(
|
||||
TestRows.upsertAllWithTtl(
|
||||
{
|
||||
tenant_id: 'tenant-expired',
|
||||
item_id: 1n,
|
||||
created_at: new Date('2026-03-01T00:00:00.000Z'),
|
||||
payload: null,
|
||||
tags: null,
|
||||
counts: null,
|
||||
local_day: null,
|
||||
note: 'temporary',
|
||||
},
|
||||
1,
|
||||
),
|
||||
);
|
||||
|
||||
await sleep(1100);
|
||||
|
||||
const visibleRows = await executor.executeQuery<TestRow>(
|
||||
TestRows.select({where: TestRows.where.eq('tenant_id')}).bind({tenant_id: 'tenant-expired'}),
|
||||
);
|
||||
expect(visibleRows).toEqual([]);
|
||||
|
||||
const physicalBefore = await client.query<{count: string}>(
|
||||
`SELECT count(*)::text AS count FROM ${quoteIdentifier(kvTable)} WHERE table_name = $1 AND row_data ->> 'tenant_id' = $2`,
|
||||
[logicalTableName, 'tenant-expired'],
|
||||
);
|
||||
expect(Number(physicalBefore.rows[0]!.count)).toBe(1);
|
||||
|
||||
const pruned = await pruneExpiredPostgresKvRows(client, 10);
|
||||
expect(pruned).toBeGreaterThanOrEqual(1);
|
||||
|
||||
const physicalAfter = await client.query<{count: string}>(
|
||||
`SELECT count(*)::text AS count FROM ${quoteIdentifier(kvTable)} WHERE table_name = $1 AND row_data ->> 'tenant_id' = $2`,
|
||||
[logicalTableName, 'tenant-expired'],
|
||||
);
|
||||
expect(Number(physicalAfter.rows[0]!.count)).toBe(0);
|
||||
});
|
||||
|
||||
test('patching a logically expired row makes it visible again without keeping stale TTL', async () => {
|
||||
await executor.executeQuery(
|
||||
TestRows.upsertAllWithTtl(
|
||||
{
|
||||
tenant_id: 'tenant-resurrect',
|
||||
item_id: 1n,
|
||||
created_at: new Date('2026-04-01T00:00:00.000Z'),
|
||||
payload: null,
|
||||
tags: null,
|
||||
counts: null,
|
||||
local_day: null,
|
||||
note: 'temporary',
|
||||
},
|
||||
1,
|
||||
),
|
||||
);
|
||||
|
||||
await sleep(1100);
|
||||
await executor.executeQuery(
|
||||
TestRows.patchByPk({tenant_id: 'tenant-resurrect', item_id: 1n}, {note: Db.set('resurrected')}),
|
||||
);
|
||||
|
||||
const rows = await executor.executeQuery<TestRow>(
|
||||
TestRows.select({
|
||||
where: [TestRows.where.eq('tenant_id'), TestRows.where.eq('item_id')],
|
||||
limit: 1,
|
||||
}).bind({tenant_id: 'tenant-resurrect', item_id: 1n}),
|
||||
);
|
||||
expect(rows).toHaveLength(1);
|
||||
expect(rows[0]!.note).toBe('resurrected');
|
||||
});
|
||||
});
|
||||
@@ -1,127 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import http from 'node:http';
|
||||
import type {AddressInfo} from 'node:net';
|
||||
import {type FakeNcmecFailure, FakeNcmecServer} from './FakeNcmecServer';
|
||||
|
||||
const host = process.env.FAKE_NCMEC_HOST ?? '127.0.0.1';
|
||||
const port = readPort('FAKE_NCMEC_PORT', 9090);
|
||||
const controlHost = process.env.FAKE_NCMEC_CONTROL_HOST ?? '127.0.0.1';
|
||||
const controlPort = readPort('FAKE_NCMEC_CONTROL_PORT', port + 1);
|
||||
const publicHost = process.env.FAKE_NCMEC_PUBLIC_HOST ?? (host === '0.0.0.0' ? '<reachable-host-or-ip>' : host);
|
||||
const username = process.env.FAKE_NCMEC_USERNAME ?? 'usr123';
|
||||
const password = process.env.FAKE_NCMEC_PASSWORD ?? 'pswd123';
|
||||
|
||||
if (host === controlHost && port === controlPort) {
|
||||
throw new Error('FAKE_NCMEC_CONTROL_PORT must differ from FAKE_NCMEC_PORT when both servers bind the same host');
|
||||
}
|
||||
|
||||
const fake = new FakeNcmecServer({username, password});
|
||||
|
||||
function log(...parts: Array<unknown>): void {
|
||||
const ts = new Date().toISOString();
|
||||
console.log(`[fake-ncmec ${ts}]`, ...parts);
|
||||
}
|
||||
|
||||
await fake.start({
|
||||
host,
|
||||
port,
|
||||
onRequest: (entry) => {
|
||||
log(
|
||||
`${entry.remoteAddress ?? '?'} ${entry.method} ${entry.path} -> ${entry.statusCode} ${entry.durationMs}ms (${entry.requestId})`,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const controlServer = http.createServer(async (req, res) => {
|
||||
const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
|
||||
log(`control ${req.method} ${url.pathname}`);
|
||||
if (req.method === 'GET' && url.pathname === '/_state') {
|
||||
res.writeHead(200, {'content-type': 'application/json'});
|
||||
res.end(JSON.stringify(fake.getReports(), null, 2));
|
||||
return;
|
||||
}
|
||||
if (req.method === 'GET' && url.pathname.startsWith('/_report/')) {
|
||||
const reportId = decodeURIComponent(url.pathname.slice('/_report/'.length));
|
||||
const report = fake.getReport(reportId);
|
||||
if (!report) {
|
||||
res.writeHead(404, {'content-type': 'application/json'});
|
||||
res.end(JSON.stringify({error: `Unknown report ${reportId}`}));
|
||||
return;
|
||||
}
|
||||
res.writeHead(200, {'content-type': 'application/json'});
|
||||
res.end(JSON.stringify(report, null, 2));
|
||||
return;
|
||||
}
|
||||
if (req.method === 'POST' && url.pathname === '/_reset') {
|
||||
fake.reset();
|
||||
res.writeHead(204);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
if (req.method === 'POST' && url.pathname === '/_fail') {
|
||||
const mode = (url.searchParams.get('mode') ?? 'none') as FakeNcmecFailure;
|
||||
if (!VALID_FAILURE_MODES.has(mode)) {
|
||||
res.writeHead(400, {'content-type': 'application/json'});
|
||||
res.end(JSON.stringify({error: `Invalid failure mode ${mode}`}));
|
||||
return;
|
||||
}
|
||||
fake.setFailure(mode);
|
||||
res.writeHead(200, {'content-type': 'application/json'});
|
||||
res.end(JSON.stringify({failure: mode}));
|
||||
return;
|
||||
}
|
||||
res.writeHead(404);
|
||||
res.end('Not found. Try GET /_state, POST /_reset, POST /_fail?mode=...');
|
||||
});
|
||||
|
||||
await new Promise<void>((resolve) => controlServer.listen(controlPort, controlHost, () => resolve()));
|
||||
|
||||
const address = controlServer.address() as AddressInfo;
|
||||
|
||||
log(`NCMEC-compatible fake server ready`);
|
||||
|
||||
log(` NCMEC bind → http://${fake.host}:${fake.port}/ispws`);
|
||||
|
||||
log(` NCMEC base URL → http://${publicHost}:${fake.port}/ispws`);
|
||||
|
||||
log(` control endpoint → http://${address.address}:${address.port}/_state`);
|
||||
|
||||
log(` credentials → ${username}:${password}`);
|
||||
|
||||
if (publicHost.startsWith('<')) {
|
||||
log(' set FAKE_NCMEC_PUBLIC_HOST to the hostname or IP your prod API can reach');
|
||||
}
|
||||
|
||||
log(' point prod Config.ncmec.baseUrl at the NCMEC base URL above');
|
||||
|
||||
function shutdown(signal: string): void {
|
||||
log(`received ${signal}, shutting down`);
|
||||
Promise.allSettled([fake.stop(), new Promise<void>((resolve) => controlServer.close(() => resolve()))]).then(() =>
|
||||
process.exit(0),
|
||||
);
|
||||
}
|
||||
|
||||
process.on('SIGINT', () => shutdown('SIGINT'));
|
||||
|
||||
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
||||
|
||||
const VALID_FAILURE_MODES = new Set<FakeNcmecFailure>([
|
||||
'none',
|
||||
'submit',
|
||||
'upload',
|
||||
'fileinfo',
|
||||
'finish',
|
||||
'retract',
|
||||
'unauthorized',
|
||||
]);
|
||||
|
||||
function readPort(name: string, fallback: number): number {
|
||||
const raw = process.env[name];
|
||||
if (!raw) return fallback;
|
||||
const value = Number.parseInt(raw, 10);
|
||||
if (!Number.isInteger(value) || value < 0 || value > 65535) {
|
||||
throw new Error(`${name} must be an integer between 0 and 65535`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -1,430 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import crypto from 'node:crypto';
|
||||
import http from 'node:http';
|
||||
import type {AddressInfo} from 'node:net';
|
||||
import {Readable} from 'node:stream';
|
||||
import {XMLParser, XMLValidator} from 'fast-xml-parser';
|
||||
|
||||
export type FakeNcmecFailure = 'none' | 'submit' | 'upload' | 'fileinfo' | 'finish' | 'retract' | 'unauthorized';
|
||||
|
||||
export interface FakeNcmecFileRecord {
|
||||
fileId: string;
|
||||
filename: string;
|
||||
size: number;
|
||||
md5: string;
|
||||
submittedDetails: boolean;
|
||||
detailsXml: string | null;
|
||||
}
|
||||
|
||||
export interface FakeNcmecReportRecord {
|
||||
reportId: string;
|
||||
reportXml: string;
|
||||
files: Array<FakeNcmecFileRecord>;
|
||||
finished: boolean;
|
||||
retracted: boolean;
|
||||
authUsername: string;
|
||||
}
|
||||
|
||||
interface FakeNcmecServerOptions {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
interface FakeNcmecServerStartOptions {
|
||||
host?: string;
|
||||
port?: number;
|
||||
onRequest?: (entry: FakeNcmecRequestLogEntry) => void;
|
||||
}
|
||||
|
||||
interface FakeNcmecRequestLogEntry {
|
||||
method: string;
|
||||
path: string;
|
||||
statusCode: number;
|
||||
requestId: string;
|
||||
durationMs: number;
|
||||
remoteAddress: string | null;
|
||||
}
|
||||
|
||||
const xmlParser = new XMLParser({ignoreAttributes: false, parseTagValue: false, trimValues: true});
|
||||
|
||||
export class FakeNcmecServer {
|
||||
private server: http.Server | null = null;
|
||||
private listenPort = 0;
|
||||
private listenHost = '127.0.0.1';
|
||||
private onRequest: ((entry: FakeNcmecRequestLogEntry) => void) | null = null;
|
||||
private reportCounter = 1000000;
|
||||
private fileCounter = 1;
|
||||
private requestCounter = 1;
|
||||
private readonly reports = new Map<string, FakeNcmecReportRecord>();
|
||||
private failureMode: FakeNcmecFailure = 'none';
|
||||
|
||||
constructor(private readonly options: FakeNcmecServerOptions) {}
|
||||
|
||||
async start(startOptions: FakeNcmecServerStartOptions = {}): Promise<void> {
|
||||
if (this.server) {
|
||||
throw new Error('FakeNcmecServer already started');
|
||||
}
|
||||
this.listenHost = startOptions.host ?? '127.0.0.1';
|
||||
this.onRequest = startOptions.onRequest ?? null;
|
||||
this.server = http.createServer((req, res) => this.handle(req, res));
|
||||
await new Promise<void>((resolve) => this.server!.listen(startOptions.port ?? 0, this.listenHost, () => resolve()));
|
||||
this.listenPort = (this.server!.address() as AddressInfo).port;
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
if (!this.server) return;
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
this.server!.close((error) => (error ? reject(error) : resolve()));
|
||||
});
|
||||
this.server = null;
|
||||
this.listenPort = 0;
|
||||
this.onRequest = null;
|
||||
}
|
||||
|
||||
get baseUrl(): string {
|
||||
if (!this.server) throw new Error('FakeNcmecServer not started');
|
||||
return `http://${this.listenHost}:${this.listenPort}/ispws`;
|
||||
}
|
||||
|
||||
get host(): string {
|
||||
if (!this.server) throw new Error('FakeNcmecServer not started');
|
||||
return this.listenHost;
|
||||
}
|
||||
|
||||
get port(): number {
|
||||
if (!this.server) throw new Error('FakeNcmecServer not started');
|
||||
return this.listenPort;
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.reports.clear();
|
||||
this.reportCounter = 1000000;
|
||||
this.fileCounter = 1;
|
||||
this.requestCounter = 1;
|
||||
this.failureMode = 'none';
|
||||
}
|
||||
|
||||
setFailure(mode: FakeNcmecFailure): void {
|
||||
this.failureMode = mode;
|
||||
}
|
||||
|
||||
getReports(): Array<FakeNcmecReportRecord> {
|
||||
return [...this.reports.values()];
|
||||
}
|
||||
|
||||
getReport(reportId: string): FakeNcmecReportRecord | null {
|
||||
return this.reports.get(reportId) ?? null;
|
||||
}
|
||||
|
||||
private async handle(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
||||
try {
|
||||
const url = new URL(req.url ?? '/', 'http://127.0.0.1');
|
||||
const pathname = url.pathname.replace(/^\/ispws/, '') || '/';
|
||||
const requestId = `req-${this.requestCounter++}`;
|
||||
const startedAt = Date.now();
|
||||
res.setHeader('Request-ID', requestId);
|
||||
res.on('finish', () => {
|
||||
this.onRequest?.({
|
||||
method: req.method ?? 'GET',
|
||||
path: pathname,
|
||||
statusCode: res.statusCode,
|
||||
requestId,
|
||||
durationMs: Date.now() - startedAt,
|
||||
remoteAddress: req.socket.remoteAddress ?? null,
|
||||
});
|
||||
});
|
||||
if (this.failureMode === 'unauthorized') {
|
||||
return this.sendStatus(res, 401, '');
|
||||
}
|
||||
if (!this.checkBasicAuth(req)) {
|
||||
return this.sendStatus(res, 401, '');
|
||||
}
|
||||
const injectedHeader = (req.headers['x-test-fail'] ?? '').toString() as FakeNcmecFailure;
|
||||
const effectiveFailure: FakeNcmecFailure = injectedHeader || this.failureMode;
|
||||
if (req.method === 'GET' && pathname === '/status') {
|
||||
const remoteIp = req.socket.remoteAddress ?? 'unknown';
|
||||
return this.sendXml(
|
||||
res,
|
||||
200,
|
||||
ok(
|
||||
`<responseDescription>Remote User : ${this.options.username}, Remote Ip : ${remoteIp}</responseDescription>`,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (req.method === 'GET' && pathname === '/xsd') {
|
||||
return this.sendText(res, 200, '<schema></schema>');
|
||||
}
|
||||
if (req.method !== 'POST') {
|
||||
return this.sendStatus(res, 405, '');
|
||||
}
|
||||
const request = await this.toWebRequest(req, url);
|
||||
switch (pathname) {
|
||||
case '/submit':
|
||||
return this.handleSubmit(request, res, effectiveFailure);
|
||||
case '/upload':
|
||||
return this.handleUpload(request, res, effectiveFailure);
|
||||
case '/fileinfo':
|
||||
return this.handleFileInfo(request, res, effectiveFailure);
|
||||
case '/finish':
|
||||
return this.handleFinish(request, res, effectiveFailure);
|
||||
case '/retract':
|
||||
return this.handleRetract(request, res, effectiveFailure);
|
||||
default:
|
||||
return this.sendStatus(res, 404, '');
|
||||
}
|
||||
} catch (error) {
|
||||
this.sendStatus(res, 500, String(error));
|
||||
}
|
||||
}
|
||||
|
||||
private async handleSubmit(request: Request, res: http.ServerResponse, failure: FakeNcmecFailure): Promise<void> {
|
||||
if (failure === 'submit') {
|
||||
return this.sendXml(res, 200, errorXml(1100, 'Save failed'));
|
||||
}
|
||||
if (!isXmlContentType(request)) {
|
||||
return this.sendXml(res, 200, errorXml(4000, 'Invalid request'));
|
||||
}
|
||||
const body = await request.text();
|
||||
if (XMLValidator.validate(body) !== true) {
|
||||
return this.sendXml(res, 200, errorXml(4110, 'Malformed XML submittal'));
|
||||
}
|
||||
const report = parseReportDocument(body);
|
||||
if (!hasRequiredReportFields(report)) {
|
||||
return this.sendXml(res, 200, errorXml(4100, 'Validation failed'));
|
||||
}
|
||||
const reportId = String(this.reportCounter++);
|
||||
this.reports.set(reportId, {
|
||||
reportId,
|
||||
reportXml: body,
|
||||
files: [],
|
||||
finished: false,
|
||||
retracted: false,
|
||||
authUsername: this.options.username,
|
||||
});
|
||||
return this.sendXml(res, 200, ok(`<reportId>${reportId}</reportId>`));
|
||||
}
|
||||
|
||||
private async handleUpload(request: Request, res: http.ServerResponse, failure: FakeNcmecFailure): Promise<void> {
|
||||
if (failure === 'upload') {
|
||||
return this.sendXml(res, 200, errorXml(1111, 'File upload failed'));
|
||||
}
|
||||
const form = await request.formData();
|
||||
const reportId = String(form.get('id') ?? '');
|
||||
const file = form.get('file');
|
||||
const record = this.reports.get(reportId);
|
||||
if (!reportId || !record) {
|
||||
return this.sendXml(res, 200, errorXml(5001, 'Report does not exist'));
|
||||
}
|
||||
if (record.retracted) {
|
||||
return this.sendXml(res, 200, errorXml(5101, 'Report already retracted'));
|
||||
}
|
||||
if (record.finished) {
|
||||
return this.sendXml(res, 200, errorXml(5102, 'Report already finished'));
|
||||
}
|
||||
if (!(file instanceof Blob)) {
|
||||
return this.sendXml(res, 200, errorXml(4200, 'Malformed file submittal'));
|
||||
}
|
||||
const buffer = Buffer.from(await file.arrayBuffer());
|
||||
const md5 = crypto.createHash('md5').update(buffer).digest('hex');
|
||||
const fileId = `file-${this.fileCounter++}`;
|
||||
record.files.push({
|
||||
fileId,
|
||||
filename: (file as File).name || 'upload.bin',
|
||||
size: buffer.byteLength,
|
||||
md5,
|
||||
submittedDetails: false,
|
||||
detailsXml: null,
|
||||
});
|
||||
return this.sendXml(res, 200, ok(`<reportId>${reportId}</reportId><fileId>${fileId}</fileId><hash>${md5}</hash>`));
|
||||
}
|
||||
|
||||
private async handleFileInfo(request: Request, res: http.ServerResponse, failure: FakeNcmecFailure): Promise<void> {
|
||||
if (failure === 'fileinfo') {
|
||||
return this.sendXml(res, 200, errorXml(1300, 'Update failed'));
|
||||
}
|
||||
if (!isXmlContentType(request)) {
|
||||
return this.sendXml(res, 200, errorXml(4000, 'Invalid request'));
|
||||
}
|
||||
const body = await request.text();
|
||||
if (XMLValidator.validate(body) !== true) {
|
||||
return this.sendXml(res, 200, errorXml(4110, 'Malformed XML submittal'));
|
||||
}
|
||||
const fileDetails = parseFileDetailsDocument(body);
|
||||
if (!hasFileDetailsFields(fileDetails)) {
|
||||
return this.sendXml(res, 200, errorXml(4100, 'Validation failed'));
|
||||
}
|
||||
const reportId = nonBlankString(fileDetails?.reportId);
|
||||
const fileId = nonBlankString(fileDetails?.fileId);
|
||||
const record = reportId ? this.reports.get(reportId) : null;
|
||||
if (!record) {
|
||||
return this.sendXml(res, 200, errorXml(5001, 'Report does not exist'));
|
||||
}
|
||||
if (record.retracted) {
|
||||
return this.sendXml(res, 200, errorXml(5101, 'Report already retracted'));
|
||||
}
|
||||
if (record.finished) {
|
||||
return this.sendXml(res, 200, errorXml(5102, 'Report already finished'));
|
||||
}
|
||||
const file = record.files.find((f) => f.fileId === fileId);
|
||||
if (!file) {
|
||||
return this.sendXml(res, 200, errorXml(5002, 'File does not exist'));
|
||||
}
|
||||
file.submittedDetails = true;
|
||||
file.detailsXml = body;
|
||||
return this.sendXml(res, 200, ok(`<reportId>${reportId}</reportId>`));
|
||||
}
|
||||
|
||||
private async handleFinish(request: Request, res: http.ServerResponse, failure: FakeNcmecFailure): Promise<void> {
|
||||
if (failure === 'finish') {
|
||||
return this.sendXml(res, 200, errorXml(1300, 'Update failed'));
|
||||
}
|
||||
const form = await request.formData();
|
||||
const reportId = String(form.get('id') ?? '');
|
||||
const record = this.reports.get(reportId);
|
||||
if (!record) {
|
||||
return this.sendXml(res, 200, errorXml(5001, 'Report does not exist'));
|
||||
}
|
||||
if (record.retracted) {
|
||||
return this.sendXml(res, 200, errorXml(5101, 'Report already retracted'));
|
||||
}
|
||||
if (record.finished) {
|
||||
return this.sendXml(res, 200, errorXml(5102, 'Report already finished'));
|
||||
}
|
||||
record.finished = true;
|
||||
const files = record.files.map((f) => ` <fileId>${f.fileId}</fileId>`).join('\n');
|
||||
const body = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<reportDoneResponse>
|
||||
<responseCode>0</responseCode>
|
||||
<reportId>${reportId}</reportId>
|
||||
<files>
|
||||
${files}
|
||||
</files>
|
||||
</reportDoneResponse>`;
|
||||
return this.sendXml(res, 200, body);
|
||||
}
|
||||
|
||||
private async handleRetract(request: Request, res: http.ServerResponse, failure: FakeNcmecFailure): Promise<void> {
|
||||
if (failure === 'retract') {
|
||||
return this.sendXml(res, 200, errorXml(1300, 'Update failed'));
|
||||
}
|
||||
const form = await request.formData();
|
||||
const reportId = String(form.get('id') ?? '');
|
||||
const record = this.reports.get(reportId);
|
||||
if (!record) {
|
||||
return this.sendXml(res, 200, errorXml(5001, 'Report does not exist'));
|
||||
}
|
||||
if (record.retracted) {
|
||||
return this.sendXml(res, 200, errorXml(5101, 'Report already retracted'));
|
||||
}
|
||||
if (record.finished) {
|
||||
return this.sendXml(res, 200, errorXml(5102, 'Report already finished'));
|
||||
}
|
||||
record.retracted = true;
|
||||
return this.sendXml(res, 200, ok(`<reportId>${reportId}</reportId>`));
|
||||
}
|
||||
|
||||
private checkBasicAuth(req: http.IncomingMessage): boolean {
|
||||
const header = req.headers.authorization ?? '';
|
||||
if (!header.toLowerCase().startsWith('basic ')) return false;
|
||||
const decoded = Buffer.from(header.slice(6), 'base64').toString('utf-8');
|
||||
const idx = decoded.indexOf(':');
|
||||
if (idx === -1) return false;
|
||||
const user = decoded.slice(0, idx);
|
||||
const pass = decoded.slice(idx + 1);
|
||||
return user === this.options.username && pass === this.options.password;
|
||||
}
|
||||
|
||||
private async toWebRequest(req: http.IncomingMessage, url: URL): Promise<Request> {
|
||||
const headers = new Headers();
|
||||
for (const [k, v] of Object.entries(req.headers)) {
|
||||
if (v === undefined) continue;
|
||||
if (Array.isArray(v)) headers.set(k, v.join(','));
|
||||
else headers.set(k, String(v));
|
||||
}
|
||||
const method = req.method ?? 'GET';
|
||||
const body = method === 'GET' || method === 'HEAD' ? undefined : (Readable.toWeb(req) as ReadableStream);
|
||||
return new Request(url, {method, headers, body, duplex: 'half'} as RequestInit & {
|
||||
duplex: 'half';
|
||||
});
|
||||
}
|
||||
|
||||
private sendXml(res: http.ServerResponse, status: number, body: string): void {
|
||||
res.writeHead(status, {'content-type': 'application/xml; charset=utf-8'});
|
||||
res.end(body);
|
||||
}
|
||||
|
||||
private sendText(res: http.ServerResponse, status: number, body: string): void {
|
||||
res.writeHead(status, {'content-type': 'text/plain; charset=utf-8'});
|
||||
res.end(body);
|
||||
}
|
||||
|
||||
private sendStatus(res: http.ServerResponse, status: number, body: string): void {
|
||||
res.writeHead(status);
|
||||
res.end(body);
|
||||
}
|
||||
}
|
||||
|
||||
function ok(extra: string): string {
|
||||
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<reportResponse>
|
||||
<responseCode>0</responseCode>
|
||||
<responseDescription>Success</responseDescription>
|
||||
${extra}
|
||||
</reportResponse>`;
|
||||
}
|
||||
|
||||
function errorXml(code: number, description: string): string {
|
||||
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<reportResponse>
|
||||
<responseCode>${code}</responseCode>
|
||||
<responseDescription>${description}</responseDescription>
|
||||
</reportResponse>`;
|
||||
}
|
||||
|
||||
function isXmlContentType(request: Request): boolean {
|
||||
return (request.headers.get('content-type') ?? '').toLowerCase().includes('text/xml');
|
||||
}
|
||||
|
||||
function parseReportDocument(xml: string): Record<string, unknown> | null {
|
||||
const parsed = xmlParser.parse(xml) as Record<string, unknown>;
|
||||
return objectField(parsed, 'report');
|
||||
}
|
||||
|
||||
function parseFileDetailsDocument(xml: string): Record<string, unknown> | null {
|
||||
const parsed = xmlParser.parse(xml) as Record<string, unknown>;
|
||||
return objectField(parsed, 'fileDetails');
|
||||
}
|
||||
|
||||
function hasRequiredReportFields(report: Record<string, unknown> | null): boolean {
|
||||
const incidentSummary = objectField(report, 'incidentSummary');
|
||||
const reporter = objectField(report, 'reporter');
|
||||
const reportingPerson = objectField(reporter, 'reportingPerson');
|
||||
return Boolean(
|
||||
nonBlankString(incidentSummary?.incidentType) &&
|
||||
nonBlankString(incidentSummary?.incidentDateTime) &&
|
||||
nonBlankString(reportingPerson?.email),
|
||||
);
|
||||
}
|
||||
|
||||
function hasFileDetailsFields(fileDetails: Record<string, unknown> | null): boolean {
|
||||
return Boolean(nonBlankString(fileDetails?.reportId) && nonBlankString(fileDetails?.fileId));
|
||||
}
|
||||
|
||||
function objectField(value: unknown, key: string): Record<string, unknown> | null {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
||||
const field = (value as Record<string, unknown>)[key];
|
||||
if (!field || typeof field !== 'object' || Array.isArray(field)) return null;
|
||||
return field as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function nonBlankString(value: unknown): string | null {
|
||||
if (typeof value === 'string') {
|
||||
const trimmed = value.trim();
|
||||
return trimmed || null;
|
||||
}
|
||||
if (typeof value === 'number' || typeof value === 'bigint') {
|
||||
return String(value);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -3,7 +3,26 @@
|
||||
import crypto from 'node:crypto';
|
||||
import {XMLParser, XMLValidator} from 'fast-xml-parser';
|
||||
import {HttpResponse, http, type RequestHandler} from 'msw';
|
||||
import type {FakeNcmecFailure, FakeNcmecFileRecord, FakeNcmecReportRecord} from '../../fake-ncmec/FakeNcmecServer';
|
||||
|
||||
type FakeNcmecFailure = 'none' | 'submit' | 'upload' | 'fileinfo' | 'finish' | 'retract' | 'unauthorized';
|
||||
|
||||
interface FakeNcmecFileRecord {
|
||||
fileId: string;
|
||||
filename: string;
|
||||
size: number;
|
||||
md5: string;
|
||||
submittedDetails: boolean;
|
||||
detailsXml: string | null;
|
||||
}
|
||||
|
||||
interface FakeNcmecReportRecord {
|
||||
reportId: string;
|
||||
reportXml: string;
|
||||
files: Array<FakeNcmecFileRecord>;
|
||||
finished: boolean;
|
||||
retracted: boolean;
|
||||
authUsername: string;
|
||||
}
|
||||
|
||||
const BASE_URL = 'https://ncmec.test/ispws';
|
||||
const USERNAME = 'usr123';
|
||||
|
||||
@@ -200,11 +200,13 @@ const nativeRuntimeFilePatterns = [
|
||||
'node_modules/@fluxer/linux-input-hook/*.node',
|
||||
'node_modules/@fluxer/platform-info/package.json',
|
||||
'node_modules/@fluxer/platform-info/index.js',
|
||||
'node_modules/@fluxer/platform-info/pure.cjs',
|
||||
'node_modules/@fluxer/platform-info/loader-diagnostics.cjs',
|
||||
'node_modules/@fluxer/platform-info/*.node',
|
||||
'node_modules/@fluxer/webauthn/package.json',
|
||||
'node_modules/@fluxer/webauthn/index.js',
|
||||
'node_modules/@fluxer/webauthn/index.d.ts',
|
||||
'node_modules/@fluxer/webauthn/pure.cjs',
|
||||
'node_modules/@fluxer/webauthn/loader-diagnostics.cjs',
|
||||
'node_modules/@fluxer/webauthn/*.node',
|
||||
'node_modules/@fluxer/webauthn/*.so*',
|
||||
@@ -213,6 +215,7 @@ const nativeRuntimeFilePatterns = [
|
||||
'node_modules/@fluxer/webrtc-sender/index.d.ts',
|
||||
'node_modules/@fluxer/webrtc-sender/*.node',
|
||||
'node_modules/.pnpm/@fluxer+*/node_modules/@fluxer/*/loader-diagnostics.cjs',
|
||||
'node_modules/.pnpm/@fluxer+*/node_modules/@fluxer/*/pure.cjs',
|
||||
'node_modules/.pnpm/@fluxer+win-process-loopback@*/node_modules/@fluxer/win-process-loopback/*.node',
|
||||
'node_modules/.pnpm/@fluxer+win-game-capture@*/node_modules/@fluxer/win-game-capture/*.node',
|
||||
'node_modules/.pnpm/@fluxer+win-game-capture@*/node_modules/@fluxer/win-game-capture/*.dll',
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
{
|
||||
"status": "skip",
|
||||
"startedAt": "2026-06-05T15:40:30.428Z",
|
||||
"endedAt": "2026-06-05T15:40:30.428Z",
|
||||
"platform": "linux",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://localhost:7880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-38884-16b7fb75-22a2-4914-8e2d-255451c5b7e9",
|
||||
"required": false,
|
||||
"strict": false,
|
||||
"timeoutMs": 30000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 0,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"expectedWidth": 320,
|
||||
"expectedHeight": 180,
|
||||
"fps": 15,
|
||||
"minFpsRatio": 0,
|
||||
"maxFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/workspaces/fluxer/fluxer_desktop/native-media-reports/2026-06-05T15-40-10-773Z/livekit-harness.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null
|
||||
},
|
||||
"checks": [],
|
||||
"skippedReason": "LiveKit is unreachable at ws://localhost:7880 (connect ECONNREFUSED 127.0.0.1:7880)",
|
||||
"error": null,
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": false,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "nvenc_not_compiled",
|
||||
"detail": "CUDA headers were not found at build time; rebuild with CUDA_HOME pointing to a CUDA toolkit that contains include/cuda.h"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 0,
|
||||
"videoCallbacks": 0,
|
||||
"videoBytes": 0,
|
||||
"receivedFps": 0,
|
||||
"requiredFps": null,
|
||||
"maxVideoFrameGapMs": 0,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"avDriftMs": null,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"lastVideoMeta": null,
|
||||
"publisherStats": null,
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": null
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
-149
File diff suppressed because one or more lines are too long
-197
@@ -1,197 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-06T22:15:58.713Z",
|
||||
"endedAt": "2026-06-06T22:16:28.718Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:7880",
|
||||
"serverApiUrl": "http://127.0.0.1:7880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-84586-8f492568-efae-42a1-a344-5a0c9599fa8d",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-84586",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-84586",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-84586",
|
||||
"required": false,
|
||||
"strict": false,
|
||||
"timeoutMs": 30000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 0,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"expectedWidth": 320,
|
||||
"expectedHeight": 180,
|
||||
"fps": 15,
|
||||
"minFpsRatio": 0,
|
||||
"maxFrameGapMs": 0,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": false,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": true,
|
||||
"secondaryPublisherScreenSimulcast": true,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": false,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-06T22-15-48Z-codex-host-smoke-signed/livekit-harness.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 320x180",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 3",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 1",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 3",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": false
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "Error: connect timed out after 30000ms\n at file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1346:9\n at async runHarness (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1773:3)\n at async main (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:2025:18)\n at async file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:2058:19",
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 0,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 0,
|
||||
"videoCallbacks": 0,
|
||||
"videoBytes": 0,
|
||||
"receivedFps": 0,
|
||||
"requiredFps": null,
|
||||
"maxVideoFrameGapMs": 0,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 0,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": null,
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": null,
|
||||
"error": null,
|
||||
"participants": []
|
||||
},
|
||||
"lastVideoMeta": null,
|
||||
"publisherStats": null,
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": null
|
||||
}
|
||||
}
|
||||
-162
File diff suppressed because one or more lines are too long
@@ -1,274 +0,0 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"startedAt": "2026-06-06T22:17:16.198Z",
|
||||
"endedAt": "2026-06-06T22:17:18.067Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:8088/livekit",
|
||||
"serverApiUrl": "http://127.0.0.1:8088",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-84689-a15db5c1-00f9-4cc5-8b1a-e68759b5e380",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-84689",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-84689",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-84689",
|
||||
"required": false,
|
||||
"strict": false,
|
||||
"timeoutMs": 30000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 0,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"expectedWidth": 320,
|
||||
"expectedHeight": 180,
|
||||
"fps": 15,
|
||||
"minFpsRatio": 0,
|
||||
"maxFrameGapMs": 0,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": false,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": true,
|
||||
"secondaryPublisherScreenSimulcast": true,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": false,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": false,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": false,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-06T22-17-15Z-codex-livekit-proxy.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 320x180",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": null,
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 13,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 0,
|
||||
"videoCallbacks": 13,
|
||||
"videoBytes": 1123200,
|
||||
"receivedFps": 0,
|
||||
"requiredFps": null,
|
||||
"maxVideoFrameGapMs": 0,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 390,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 5,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 15,
|
||||
"startedAtMs": 1780784237058,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 16,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 69,
|
||||
"lastPushAtMs": 1780784238060,
|
||||
"elapsedMs": 1018,
|
||||
"producedFps": 15.72
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": null,
|
||||
"error": null,
|
||||
"participants": []
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_NicBnT5mxvvm",
|
||||
"trackSid": "TR_VSrvFY4wSg7eLt",
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"timestampUs": 335522961000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VSrvFY4wSg7eLt",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 0,
|
||||
"packetsLost": 0,
|
||||
"fps": 15,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"sourceWidth": 320,
|
||||
"sourceHeight": 180,
|
||||
"targetBitrateKbps": 400,
|
||||
"configuredFps": 15,
|
||||
"targetFps": 15,
|
||||
"effectiveFps": 14.6,
|
||||
"framesProduced": 14,
|
||||
"framesAccepted": 14,
|
||||
"framesDropped": 0,
|
||||
"framesCoalesced": 0,
|
||||
"framesCaptured": 14,
|
||||
"captureFailures": 0,
|
||||
"maxQueueAgeMs": 26,
|
||||
"maxPushLatencyMs": 26,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable",
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 14,
|
||||
"outgoingVideoFramesAccepted": 14,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 14,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 14.56,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 26,
|
||||
"outgoingVideoMaxPushLatencyMs": 26,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 2,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_NicBnT5mxvvm",
|
||||
"trackSid": "TR_VSrvFY4wSg7eLt",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 0,
|
||||
"packetsLost": 5,
|
||||
"jitterMs": 4,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-321
@@ -1,321 +0,0 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"startedAt": "2026-06-06T22:17:32.639Z",
|
||||
"endedAt": "2026-06-06T22:17:34.704Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:8088/livekit",
|
||||
"serverApiUrl": "http://127.0.0.1:8088",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-84848-43854e34-d3a9-4e33-bf45-b7ef3611083a",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-84848",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-84848",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-84848",
|
||||
"required": false,
|
||||
"strict": false,
|
||||
"timeoutMs": 30000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 0,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"expectedWidth": 320,
|
||||
"expectedHeight": 180,
|
||||
"fps": 15,
|
||||
"minFpsRatio": 0,
|
||||
"maxFrameGapMs": 0,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": false,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": true,
|
||||
"secondaryPublisherScreenSimulcast": true,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": false,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-06T22-17-25Z-codex-host-smoke-proxy/livekit-harness.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 320x180",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": null,
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 72,
|
||||
"inboundVideoFrames": 13,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 0,
|
||||
"videoCallbacks": 13,
|
||||
"videoBytes": 1123200,
|
||||
"receivedFps": 0,
|
||||
"requiredFps": null,
|
||||
"maxVideoFrameGapMs": 0,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 184,
|
||||
"maxAudioFrameGapOverallMs": 102,
|
||||
"avDriftMs": 0,
|
||||
"maxObservedPacketLoss": 3,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 15,
|
||||
"startedAtMs": 1780784253698,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 16,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 68,
|
||||
"lastPushAtMs": 1780784254699,
|
||||
"elapsedMs": 1014,
|
||||
"producedFps": 15.78
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": null,
|
||||
"error": null,
|
||||
"participants": []
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_HwsqmPnPxvGD",
|
||||
"trackSid": "TR_VS8RxgcaoR5kbz",
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"timestampUs": 335539594000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VS8RxgcaoR5kbz",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 0,
|
||||
"packetsLost": 0,
|
||||
"fps": 16,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"sourceWidth": 320,
|
||||
"sourceHeight": 180,
|
||||
"targetBitrateKbps": 400,
|
||||
"configuredFps": 15,
|
||||
"targetFps": 15,
|
||||
"effectiveFps": 14.6,
|
||||
"framesProduced": 6,
|
||||
"framesAccepted": 6,
|
||||
"framesDropped": 0,
|
||||
"framesCoalesced": 0,
|
||||
"framesCaptured": 6,
|
||||
"captureFailures": 0,
|
||||
"maxQueueAgeMs": 8,
|
||||
"maxPushLatencyMs": 8,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable",
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMBPL59S6qUeEf",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 0,
|
||||
"packetsLost": 0,
|
||||
"audioLevel": 0.305,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 6,
|
||||
"outgoingVideoFramesAccepted": 6,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 6,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 14.62,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 8,
|
||||
"outgoingVideoMaxPushLatencyMs": 8,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 23,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_HwsqmPnPxvGD",
|
||||
"trackSid": "TR_AMBPL59S6qUeEf",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 0,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.315,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_HwsqmPnPxvGD",
|
||||
"trackSid": "TR_VS8RxgcaoR5kbz",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 0,
|
||||
"packetsLost": 3,
|
||||
"jitterMs": 4,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-227
File diff suppressed because one or more lines are too long
@@ -1,143 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"mode": "smoke",
|
||||
"strict": false,
|
||||
"platform": "linux",
|
||||
"arch": "arm64",
|
||||
"hostname": "d4e5fefa7fc7",
|
||||
"startedAt": "2026-06-07T14:52:39.689Z",
|
||||
"endedAt": "2026-06-07T14:53:04.641Z",
|
||||
"reportDir": "/workspaces/fluxer/fluxer_desktop/native-media-reports/2026-06-07T14-52-39-687Z",
|
||||
"gates": [
|
||||
{
|
||||
"name": "mode-valid",
|
||||
"status": "pass",
|
||||
"mode": "smoke"
|
||||
},
|
||||
{
|
||||
"name": "documentation-current",
|
||||
"status": "pass",
|
||||
"missing": []
|
||||
},
|
||||
{
|
||||
"name": "strict-prerequisites",
|
||||
"status": "skip",
|
||||
"reason": "smoke mode"
|
||||
},
|
||||
{
|
||||
"name": "strict-command-plan",
|
||||
"status": "skip",
|
||||
"reason": "smoke mode"
|
||||
}
|
||||
],
|
||||
"plannedCommands": [
|
||||
{
|
||||
"name": "main-process-native-media-unit-tests",
|
||||
"command": "node --test src/main/NativeVoiceEngine.test.mjs src/main/NativeScreenCapture.test.mjs",
|
||||
"category": "shared",
|
||||
"cwd": "."
|
||||
},
|
||||
{
|
||||
"name": "desktop-typecheck",
|
||||
"command": "pnpm typecheck",
|
||||
"category": "shared",
|
||||
"cwd": "."
|
||||
},
|
||||
{
|
||||
"name": "webrtc-sender-build",
|
||||
"command": "pnpm --dir native/webrtc-sender build",
|
||||
"category": "native-sender",
|
||||
"cwd": "."
|
||||
},
|
||||
{
|
||||
"name": "webrtc-sender-tests",
|
||||
"command": "pnpm --dir native/webrtc-sender test",
|
||||
"category": "native-sender",
|
||||
"cwd": "."
|
||||
},
|
||||
{
|
||||
"name": "webrtc-sender-livekit-harness",
|
||||
"command": "pnpm --dir native/webrtc-sender test:livekit",
|
||||
"category": "livekit",
|
||||
"cwd": "."
|
||||
},
|
||||
{
|
||||
"name": "linux-screen-capture-build",
|
||||
"command": "pnpm --dir native/linux-screen-capture build",
|
||||
"category": "platform-native",
|
||||
"cwd": "."
|
||||
},
|
||||
{
|
||||
"name": "linux-screen-capture-tests",
|
||||
"command": "pnpm --dir native/linux-screen-capture test",
|
||||
"category": "platform-native",
|
||||
"cwd": "."
|
||||
},
|
||||
{
|
||||
"name": "linux-audio-capture-build",
|
||||
"command": "pnpm --dir native/linux-audio-capture build",
|
||||
"category": "platform-native",
|
||||
"cwd": "."
|
||||
},
|
||||
{
|
||||
"name": "linux-audio-capture-tests",
|
||||
"command": "pnpm --dir native/linux-audio-capture test",
|
||||
"category": "platform-native",
|
||||
"cwd": "."
|
||||
}
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"name": "main-process-native-media-unit-tests",
|
||||
"command": "node --test src/main/NativeVoiceEngine.test.mjs src/main/NativeScreenCapture.test.mjs",
|
||||
"category": "shared",
|
||||
"status": "pass",
|
||||
"code": 0,
|
||||
"signal": null,
|
||||
"startedAt": "2026-06-07T14:52:39.690Z",
|
||||
"endedAt": "2026-06-07T14:52:39.792Z",
|
||||
"durationMs": 102,
|
||||
"stdoutTail": "▶ NativeScreenCapture source identity and capability reporting\n ✔ normalizes display and window sources without changing source ids (1.614801ms)\n ✔ starts display and window captures with exact source id and kind and reports diagnostics (1.041547ms)\n ✔ fails fast when native frame sink is required but unavailable (0.500503ms)\n ✔ passes caller-provided capture id and native sink handle to the platform wrapper (0.467711ms)\n ✔ reports macOS and Linux display/window capture capabilities from platform backends (0.625753ms)\n✔ NativeScreenCapture source identity and capability reporting (4.771278ms)\n▶ NativeVoiceEngine connection lifecycle\n ✔ does not forward native events after disconnecting a pending connect (3.117018ms)\n ✔ disconnects stale native connect completion after a replacement connect starts (1.775302ms)\n✔ NativeVoiceEngine connection lifecycle (5.440365ms)\n▶ NativeVoiceEngine screen frame routing\n ✔ publishes screen share with source pacing and exposes a native frame sink handle (0.897838ms)\n ✔ keeps screen-share audio published when screen video is unpublished (0.634254ms)\n ✔ replaces screen captures without releasing screen-share audio or duplicating priority (0.502586ms)\n ✔ forwards watch and unwatch operations with source, enablement, and quality intact (0.563378ms)\n ✔ reports VideoToolbox hardware capability from the native addon (0.49767ms)\n ✔ reports native voice engine capabilities from the native addon (0.56267ms)\n ✔ maps native addon microphone not-connected errors to the typed operation code (0.639129ms)\n✔ NativeVoiceEngine screen frame routing (4.546943ms)\nℹ tests 14\nℹ suites 3\nℹ pass 14\nℹ fail 0\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 81.841144\n",
|
||||
"stderrTail": ""
|
||||
},
|
||||
{
|
||||
"name": "desktop-typecheck",
|
||||
"command": "pnpm typecheck",
|
||||
"category": "shared",
|
||||
"status": "pass",
|
||||
"code": 0,
|
||||
"signal": null,
|
||||
"startedAt": "2026-06-07T14:52:39.792Z",
|
||||
"endedAt": "2026-06-07T14:52:40.133Z",
|
||||
"durationMs": 341,
|
||||
"stdoutTail": "\n> fluxer_desktop@0.0.0 typecheck /workspaces/fluxer/fluxer_desktop\n> tsgo --noEmit\n\n",
|
||||
"stderrTail": ""
|
||||
},
|
||||
{
|
||||
"name": "webrtc-sender-build",
|
||||
"command": "pnpm --dir native/webrtc-sender build",
|
||||
"category": "native-sender",
|
||||
"status": "pass",
|
||||
"code": 0,
|
||||
"signal": null,
|
||||
"startedAt": "2026-06-07T14:52:40.133Z",
|
||||
"endedAt": "2026-06-07T14:52:57.557Z",
|
||||
"durationMs": 17424,
|
||||
"stdoutTail": "\n> @fluxer/webrtc-sender@0.0.0 build /workspaces/fluxer/fluxer_desktop/native/webrtc-sender\n> cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon\n\n[buildRustNodeAddon] cwd=/workspaces/fluxer/fluxer_desktop/native/webrtc-sender\n+ /home/vscode/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/bin/cargo build --release --target aarch64-unknown-linux-gnu --manifest-path Cargo.toml --features camera-native\n",
|
||||
"stderrTail": " Compiling crossbeam-utils v0.8.21\n Compiling fluxer_screen_frame_bus v0.0.0 (/workspaces/fluxer/fluxer_desktop/native/screen-frame-bus)\n Compiling fluxer_voice_core v0.1.0 (/workspaces/fluxer/fluxer_voice_core)\nwarning: unused import: `enum_dispatch`\n --> vendor/libwebrtc/src/audio_source.rs:15:13\n |\n15 | use crate::{enum_dispatch, imp::audio_source as imp_as};\n | ^^^^^^^^^^^^^\n |\n = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default\n\nwarning: unnecessary `unsafe` block\n --> vendor/libwebrtc/src/native/apm.rs:32:25\n |\n32 | sys_handle: unsafe {\n | ^^^^^^ unnecessary `unsafe` block\n |\n = note: `#[warn(unused_unsafe)]` (part of `#[warn(unused)]`) on by default\n\nwarning: variable does not need to be mutable\n --> vendor/libwebrtc/src/native/desktop_capturer.rs:66:13\n |\n66 | let mut sys_handle = sys_dc::ffi::DesktopCapturerOptions {\n | ----^^^^^^^^^^\n | |\n | help: remove this `mut`\n |\n = note: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default\n\nwarning: hiding a lifetime that's elided elsewhere is confusing\n --> vendor/libwebrtc/src/native/audio_mixer.rs:30:34\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option<AudioFrame>;\n | ^^^^^ the lifetime is elided here ^^^^^^^^^^ the same lifetime is hidden here\n |\n = help: the same lifetime is referred to in inconsistent ways, making the signature confusing\n = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default\nhelp: use `'_` for type paths\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option<AudioFrame<'_>>;\n | ++++\n\nwarning: `libwebrtc` (lib) generated 4 warnings (run `cargo fix --lib -p libwebrtc` to apply 3 suggestions)\n Compiling crossbeam-queue v0.3.12\n Compiling fluxer_webrtc_sender v0.0.0 (/workspaces/fluxer/fluxer_desktop/native/webrtc-sender)\n Finished `release` profile [optimized] target(s) in 10.39s\n"
|
||||
},
|
||||
{
|
||||
"name": "webrtc-sender-tests",
|
||||
"command": "pnpm --dir native/webrtc-sender test",
|
||||
"category": "native-sender",
|
||||
"status": "fail",
|
||||
"code": 1,
|
||||
"signal": null,
|
||||
"startedAt": "2026-06-07T14:52:57.557Z",
|
||||
"endedAt": "2026-06-07T14:53:04.641Z",
|
||||
"durationMs": 7084,
|
||||
"stdoutTail": "\n> @fluxer/webrtc-sender@0.0.0 test /workspaces/fluxer/fluxer_desktop/native/webrtc-sender\n> cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- test-webrtc-sender-rust && node --test index.test.mjs scripts/livekit-harness.test.mjs\n\n+ /home/vscode/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/bin/cargo test --features \"publisher,camera-native\"\n ELIFECYCLE Test failed. See above for more details.\n WARN Local package.json exists, but node_modules missing, did you mean to install?\n",
|
||||
"stderrTail": " Compiling zerocopy-derive v0.8.50\n Compiling serde v1.0.228\n Compiling ring v0.17.14\n Compiling serde_urlencoded v0.7.1\n Compiling fluxer_screen_frame_bus v0.0.0 (/workspaces/fluxer/fluxer_desktop/native/screen-frame-bus)\n Compiling rustls v0.23.40\n Compiling pbjson v0.6.0\n Compiling os_info v3.15.0\n Compiling fluxer_voice_core v0.1.0 (/workspaces/fluxer/fluxer_voice_core)\n Compiling tinytemplate v1.2.1\n Compiling pbjson-types v0.6.0\n Compiling zerocopy v0.8.50\n Compiling rustls-webpki v0.103.13\n Compiling livekit-protocol v0.7.8\n Compiling ppv-lite86 v0.2.21\n Compiling half v2.7.1\n Compiling rand_chacha v0.9.0\n Compiling rand_chacha v0.3.1\n Compiling ciborium-ll v0.2.2\n Compiling tokio-rustls v0.26.4\n Compiling rand v0.8.6\n Compiling rand v0.9.4\nerror[E0786]: found invalid metadata files for crate `rustls`\n --> /home/vscode/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-rustls-0.26.4/src/lib.rs:49:9\n |\n49 | pub use rustls;\n | ^^^^^^\n |\n = note: failed to open rmeta metadata: '/workspaces/fluxer/fluxer_desktop/native/webrtc-sender/target/debug/deps/librustls-f66081aae860cdad.rmeta'\n\nFor more information about this error, try `rustc --explain E0786`.\nerror: could not compile `tokio-rustls` (lib) due to 1 previous error\nwarning: build failed, waiting for other jobs to finish...\nCommand failed with exit code 101: /home/vscode/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/bin/cargo test --features \"publisher,camera-native\"\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
-765
@@ -1,765 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-09T04:40:52.879Z",
|
||||
"endedAt": "2026-06-09T04:41:02.390Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://localhost:7880",
|
||||
"serverApiUrl": "http://localhost:7880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-76954-91cb528a-6d21-4940-ad58-aa5c5e746211",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-76954",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-76954",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-76954",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 30000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 5000,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"expectedWidth": 320,
|
||||
"expectedHeight": 180,
|
||||
"fps": 15,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 80,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": true,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": true,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": true,
|
||||
"screenSimulcast": true,
|
||||
"secondaryPublisherScreenSimulcast": true,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": true,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-09T-codex-macos-livekit-strict5-rerun.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8-with-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 9",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 320x180",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 4",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local camera publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote camera subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound camera stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound secondary codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber secondary data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher camera publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "received FPS 10.01 below 14.25",
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 3416,
|
||||
"inboundVideoFrames": 319,
|
||||
"strictAudioFrames": 50,
|
||||
"strictVideoFrames": 50,
|
||||
"videoCallbacks": 319,
|
||||
"videoBytes": 55728000,
|
||||
"receivedFps": 10.01,
|
||||
"requiredFps": 14.25,
|
||||
"maxVideoFrameGapMs": 104,
|
||||
"maxAudioFrameGapMs": 102,
|
||||
"maxVideoFrameGapOverallMs": 83,
|
||||
"maxAudioFrameGapOverallMs": 102,
|
||||
"avDriftMs": 36,
|
||||
"maxObservedPacketLoss": 76,
|
||||
"maxObservedPacketLossDelta": 43,
|
||||
"videoResolutionCounts": {
|
||||
"320x180": 50
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 15,
|
||||
"startedAtMs": 1780980053827,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 0,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 0,
|
||||
"lastPushAtMs": null,
|
||||
"elapsedMs": 8570,
|
||||
"producedFps": 0
|
||||
},
|
||||
"secondaryPublisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 15,
|
||||
"startedAtMs": 1780980053827,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 0,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 0,
|
||||
"lastPushAtMs": null,
|
||||
"elapsedMs": 8570,
|
||||
"producedFps": 0
|
||||
},
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780980057383,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-2-76954",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSwHXmU7ctxa87",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screen_share",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMnWff3NPczSoR",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsTFRWHNvJ7MgE",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screen_share_audio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-76954",
|
||||
"trackCount": 4,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSp9aehQCmDiq4",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screen_share",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AM8ZuGaDACq9FB",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsD2wGcUaXveFj",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screen_share_audio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_VC7GWwmaJQZJio",
|
||||
"name": "camera",
|
||||
"kind": "video",
|
||||
"source": "camera",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-76954",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_szoL7dTduPBR",
|
||||
"trackSid": "TR_VC7GWwmaJQZJio",
|
||||
"trackName": "camera",
|
||||
"source": "camera",
|
||||
"width": 480,
|
||||
"height": 360,
|
||||
"timestampUs": 526246627000,
|
||||
"identity": "fluxer-native-publisher-76954",
|
||||
"kind": "video"
|
||||
},
|
||||
"lastStrictVideoMeta": {
|
||||
"atMs": 1780980062302,
|
||||
"identity": "fluxer-native-publisher-76954",
|
||||
"kind": "video",
|
||||
"source": "screen_share",
|
||||
"trackName": "screen",
|
||||
"trackSid": "TR_VSp9aehQCmDiq4",
|
||||
"width": 320,
|
||||
"height": 180
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VC7GWwmaJQZJio",
|
||||
"source": "camera",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 158.8,
|
||||
"packetsLost": 0,
|
||||
"fps": 25,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VC7GWwmaJQZJio",
|
||||
"source": "camera",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 112.8,
|
||||
"packetsLost": 0,
|
||||
"fps": 20,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AsD2wGcUaXveFj",
|
||||
"source": "screen_share_audio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 260.8,
|
||||
"packetsLost": 0,
|
||||
"audioLevel": 0.305,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AM8ZuGaDACq9FB",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.8,
|
||||
"packetsLost": 0,
|
||||
"audioLevel": 0.305,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSp9aehQCmDiq4",
|
||||
"source": "screen_share",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 7.2,
|
||||
"packetsLost": 0,
|
||||
"fps": 9,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"sourceWidth": 320,
|
||||
"sourceHeight": 180,
|
||||
"targetBitrateKbps": 400,
|
||||
"configuredFps": 15,
|
||||
"targetFps": 15,
|
||||
"effectiveFps": 0,
|
||||
"framesProduced": 0,
|
||||
"framesAccepted": 0,
|
||||
"framesDropped": 0,
|
||||
"framesCoalesced": 0,
|
||||
"framesCaptured": 0,
|
||||
"captureFailures": 0,
|
||||
"maxQueueAgeMs": 0,
|
||||
"maxPushLatencyMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable",
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_RnrcQgHxNGSb",
|
||||
"trackSid": "TR_AMnWff3NPczSoR",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 94.8,
|
||||
"packetsLost": 32,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_RnrcQgHxNGSb",
|
||||
"trackSid": "TR_AsTFRWHNvJ7MgE",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 236,
|
||||
"packetsLost": 28,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.336,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_RnrcQgHxNGSb",
|
||||
"trackSid": "TR_VSwHXmU7ctxa87",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 7.1,
|
||||
"packetsLost": 5,
|
||||
"jitterMs": 1,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoQueueCapacity": 8,
|
||||
"outgoingVideoMaxQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoPacingTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingVideoPacingMode": "source",
|
||||
"outgoingVideoBusActive": false,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 55,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AMnWff3NPczSoR",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99.2,
|
||||
"packetsLost": 0,
|
||||
"audioLevel": 0.305,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSwHXmU7ctxa87",
|
||||
"source": "screen_share",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 10.6,
|
||||
"packetsLost": 0,
|
||||
"fps": 9,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"sourceWidth": 320,
|
||||
"sourceHeight": 180,
|
||||
"targetBitrateKbps": 400,
|
||||
"configuredFps": 15,
|
||||
"targetFps": 15,
|
||||
"effectiveFps": 0,
|
||||
"framesProduced": 0,
|
||||
"framesAccepted": 0,
|
||||
"framesDropped": 0,
|
||||
"framesCoalesced": 0,
|
||||
"framesCaptured": 0,
|
||||
"captureFailures": 0,
|
||||
"maxQueueAgeMs": 0,
|
||||
"maxPushLatencyMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable",
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AsTFRWHNvJ7MgE",
|
||||
"source": "screen_share_audio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 261.1,
|
||||
"packetsLost": 0,
|
||||
"audioLevel": 0.305,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_szoL7dTduPBR",
|
||||
"trackSid": "TR_AM8ZuGaDACq9FB",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 88.9,
|
||||
"packetsLost": 25,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.331,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_szoL7dTduPBR",
|
||||
"trackSid": "TR_AsD2wGcUaXveFj",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 250.9,
|
||||
"packetsLost": 30,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_szoL7dTduPBR",
|
||||
"trackSid": "TR_VC7GWwmaJQZJio",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 113.7,
|
||||
"packetsLost": 7,
|
||||
"jitterMs": 0,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_szoL7dTduPBR",
|
||||
"trackSid": "TR_VSp9aehQCmDiq4",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 9.3,
|
||||
"packetsLost": 14,
|
||||
"jitterMs": 1,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoQueueCapacity": 8,
|
||||
"outgoingVideoMaxQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoPacingTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingVideoPacingMode": "source",
|
||||
"outgoingVideoBusActive": false,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 56,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_szoL7dTduPBR",
|
||||
"trackSid": "TR_AsD2wGcUaXveFj",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 260.5,
|
||||
"packetsLost": 5,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.318,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_szoL7dTduPBR",
|
||||
"trackSid": "TR_VC7GWwmaJQZJio",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 112.7,
|
||||
"packetsLost": 1,
|
||||
"jitterMs": 0,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_szoL7dTduPBR",
|
||||
"trackSid": "TR_AM8ZuGaDACq9FB",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.7,
|
||||
"packetsLost": 1,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.313,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_szoL7dTduPBR",
|
||||
"trackSid": "TR_VSp9aehQCmDiq4",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 7.2,
|
||||
"packetsLost": 10,
|
||||
"jitterMs": 0,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_RnrcQgHxNGSb",
|
||||
"trackSid": "TR_VSwHXmU7ctxa87",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 7.7,
|
||||
"packetsLost": 3,
|
||||
"jitterMs": 1,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_RnrcQgHxNGSb",
|
||||
"trackSid": "TR_AMnWff3NPczSoR",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.7,
|
||||
"packetsLost": 3,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.323,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_RnrcQgHxNGSb",
|
||||
"trackSid": "TR_AsTFRWHNvJ7MgE",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 267.5,
|
||||
"packetsLost": 1,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoQueueCapacity": 0,
|
||||
"outgoingVideoMaxQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoPacingTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingVideoPacingMode": "idle",
|
||||
"outgoingVideoBusActive": false,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-752
@@ -1,752 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-09T04:11:22.147Z",
|
||||
"endedAt": "2026-06-09T04:11:46.733Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://localhost:7880",
|
||||
"serverApiUrl": "http://localhost:7880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-39834-83ba9452-cd15-4567-a196-47587b5a4a47",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-39834",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-39834",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-39834",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 30000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 5000,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"expectedWidth": 320,
|
||||
"expectedHeight": 180,
|
||||
"fps": 15,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 80,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": true,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": true,
|
||||
"secondaryPublisherScreenSimulcast": true,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": true,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-livekit-strict5-localhost.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8-with-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 9",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 320x180",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 4",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local camera publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote camera subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound camera stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound secondary codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber secondary data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher camera publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "A/V drift 97ms exceeds 80ms; observed 101 video frames outside 320x180",
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 2952,
|
||||
"inboundVideoFrames": 268,
|
||||
"strictAudioFrames": 49,
|
||||
"strictVideoFrames": 201,
|
||||
"videoCallbacks": 268,
|
||||
"videoBytes": 46684800,
|
||||
"receivedFps": 40.18,
|
||||
"requiredFps": 14.25,
|
||||
"maxVideoFrameGapMs": 71,
|
||||
"maxAudioFrameGapMs": 101,
|
||||
"maxVideoFrameGapOverallMs": 242,
|
||||
"maxAudioFrameGapOverallMs": 102,
|
||||
"avDriftMs": 97,
|
||||
"maxObservedPacketLoss": 49,
|
||||
"maxObservedPacketLossDelta": 30,
|
||||
"videoResolutionCounts": {
|
||||
"480x360": 101,
|
||||
"320x180": 100
|
||||
},
|
||||
"videoResolutionMismatchCount": 101,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 15,
|
||||
"startedAtMs": 1780978299271,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 0,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 0,
|
||||
"lastPushAtMs": null,
|
||||
"elapsedMs": 7471,
|
||||
"producedFps": 0
|
||||
},
|
||||
"secondaryPublisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 15,
|
||||
"startedAtMs": 1780978299271,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 0,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 0,
|
||||
"lastPushAtMs": null,
|
||||
"elapsedMs": 7471,
|
||||
"producedFps": 0
|
||||
},
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780978301481,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-39834",
|
||||
"trackCount": 4,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSgsehdpyfYRtH",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screen_share",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMRH3WDE9scsXz",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_As4hoJPNkjh8Rv",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screen_share_audio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_VC9jnXZixHviLZ",
|
||||
"name": "camera",
|
||||
"kind": "video",
|
||||
"source": "camera",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-2-39834",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSQsd4UAwpwKy7",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screen_share",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsHmVYBSWSQdfU",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screen_share_audio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMq8at99MRi83a",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-39834",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_d6p3en8AQ9YP",
|
||||
"trackSid": "TR_VSgsehdpyfYRtH",
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"timestampUs": 524491017000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VSgsehdpyfYRtH",
|
||||
"source": "screen_share",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 6.9,
|
||||
"packetsLost": 0,
|
||||
"fps": 10,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"sourceWidth": 320,
|
||||
"sourceHeight": 180,
|
||||
"targetBitrateKbps": 400,
|
||||
"configuredFps": 15,
|
||||
"targetFps": 15,
|
||||
"effectiveFps": 0,
|
||||
"framesProduced": 0,
|
||||
"framesAccepted": 0,
|
||||
"framesDropped": 0,
|
||||
"framesCoalesced": 0,
|
||||
"framesCaptured": 0,
|
||||
"captureFailures": 0,
|
||||
"maxQueueAgeMs": 0,
|
||||
"maxPushLatencyMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable",
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_As4hoJPNkjh8Rv",
|
||||
"source": "screen_share_audio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 261.2,
|
||||
"packetsLost": 0,
|
||||
"audioLevel": 0.305,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMRH3WDE9scsXz",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.9,
|
||||
"packetsLost": 0,
|
||||
"audioLevel": 0.305,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VC9jnXZixHviLZ",
|
||||
"source": "camera",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 114.9,
|
||||
"packetsLost": 0,
|
||||
"fps": 25,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VC9jnXZixHviLZ",
|
||||
"source": "camera",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 92,
|
||||
"packetsLost": 0,
|
||||
"fps": 20,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_ychdbeoTL2ht",
|
||||
"trackSid": "TR_AMq8at99MRi83a",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.8,
|
||||
"packetsLost": 1,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.306,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_ychdbeoTL2ht",
|
||||
"trackSid": "TR_AsHmVYBSWSQdfU",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 261.4,
|
||||
"packetsLost": 3,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.344,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_ychdbeoTL2ht",
|
||||
"trackSid": "TR_VSQsd4UAwpwKy7",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 13.1,
|
||||
"packetsLost": 3,
|
||||
"jitterMs": 6,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoQueueCapacity": 8,
|
||||
"outgoingVideoMaxQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoPacingTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingVideoPacingMode": "source",
|
||||
"outgoingVideoBusActive": false,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 29,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VSQsd4UAwpwKy7",
|
||||
"source": "screen_share",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 15.3,
|
||||
"packetsLost": 0,
|
||||
"fps": 10,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"sourceWidth": 320,
|
||||
"sourceHeight": 180,
|
||||
"targetBitrateKbps": 400,
|
||||
"configuredFps": 15,
|
||||
"targetFps": 15,
|
||||
"effectiveFps": 0,
|
||||
"framesProduced": 0,
|
||||
"framesAccepted": 0,
|
||||
"framesDropped": 0,
|
||||
"framesCoalesced": 0,
|
||||
"framesCaptured": 0,
|
||||
"captureFailures": 0,
|
||||
"maxQueueAgeMs": 0,
|
||||
"maxPushLatencyMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable",
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMq8at99MRi83a",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.8,
|
||||
"packetsLost": 0,
|
||||
"audioLevel": 0.305,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AsHmVYBSWSQdfU",
|
||||
"source": "screen_share_audio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 261.2,
|
||||
"packetsLost": 0,
|
||||
"audioLevel": 0.305,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_d6p3en8AQ9YP",
|
||||
"trackSid": "TR_As4hoJPNkjh8Rv",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 254.9,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.306,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_d6p3en8AQ9YP",
|
||||
"trackSid": "TR_AMRH3WDE9scsXz",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.8,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_d6p3en8AQ9YP",
|
||||
"trackSid": "TR_VSgsehdpyfYRtH",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 7.3,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_d6p3en8AQ9YP",
|
||||
"trackSid": "TR_VC9jnXZixHviLZ",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 96.7,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 0,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoQueueCapacity": 8,
|
||||
"outgoingVideoMaxQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoPacingTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingVideoPacingMode": "source",
|
||||
"outgoingVideoBusActive": false,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 29,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_d6p3en8AQ9YP",
|
||||
"trackSid": "TR_VSgsehdpyfYRtH",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 7.3,
|
||||
"packetsLost": 1,
|
||||
"jitterMs": 2,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_d6p3en8AQ9YP",
|
||||
"trackSid": "TR_AMRH3WDE9scsXz",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.8,
|
||||
"packetsLost": 3,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.306,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_d6p3en8AQ9YP",
|
||||
"trackSid": "TR_VC9jnXZixHviLZ",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 96.6,
|
||||
"packetsLost": 1,
|
||||
"jitterMs": 0,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_d6p3en8AQ9YP",
|
||||
"trackSid": "TR_As4hoJPNkjh8Rv",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 259.7,
|
||||
"packetsLost": 1,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.306,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_ychdbeoTL2ht",
|
||||
"trackSid": "TR_AsHmVYBSWSQdfU",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 242.6,
|
||||
"packetsLost": 22,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_ychdbeoTL2ht",
|
||||
"trackSid": "TR_VSQsd4UAwpwKy7",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 15.3,
|
||||
"packetsLost": 2,
|
||||
"jitterMs": 7,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_ychdbeoTL2ht",
|
||||
"trackSid": "TR_AMq8at99MRi83a",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 96.8,
|
||||
"packetsLost": 19,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.322,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoQueueCapacity": 0,
|
||||
"outgoingVideoMaxQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoPacingTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingVideoPacingMode": "idle",
|
||||
"outgoingVideoBusActive": false,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,309 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-09T04:10:37.354Z",
|
||||
"endedAt": "2026-06-09T04:11:07.359Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:7880",
|
||||
"serverApiUrl": "http://127.0.0.1:7880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-39654-62723187-aa62-4359-9c19-cbc0403532e3",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-39654",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-39654",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-39654",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 30000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 5000,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"expectedWidth": 320,
|
||||
"expectedHeight": 180,
|
||||
"fps": 15,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 80,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": true,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": true,
|
||||
"secondaryPublisherScreenSimulcast": true,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": true,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-livekit-strict5.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8-with-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 9",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 3",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 320x180",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 12",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 4",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 12",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher local camera publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote camera subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound camera stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher connected",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screenshare publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screenshare subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare codec video/VP8",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound secondary codec video/VP8",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local microphone publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary microphone subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound microphone stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screen-share audio publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screen-share audio subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screen-share audio stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber secondary data packet received",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher camera publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screenshare publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher microphone publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screen-share audio publication",
|
||||
"pass": false
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "Error: connect timed out after 30000ms\n at file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1252:9\n at async runHarness (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1679:3)\n at async main (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1931:18)\n at async file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1964:19",
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 0,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 0,
|
||||
"videoCallbacks": 0,
|
||||
"videoBytes": 0,
|
||||
"receivedFps": 0,
|
||||
"requiredFps": 14.25,
|
||||
"maxVideoFrameGapMs": 0,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 0,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": null,
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": null,
|
||||
"error": null,
|
||||
"participants": []
|
||||
},
|
||||
"lastVideoMeta": null,
|
||||
"publisherStats": null,
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": null
|
||||
}
|
||||
}
|
||||
-331
@@ -1,331 +0,0 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"startedAt": "2026-06-09T04:09:48.418Z",
|
||||
"endedAt": "2026-06-09T04:09:50.279Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://localhost:7880",
|
||||
"serverApiUrl": "http://localhost:7880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-39291-5ee61a43-3009-4112-bca3-ae4b4667a371",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-39291",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-39291",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-39291",
|
||||
"required": false,
|
||||
"strict": false,
|
||||
"timeoutMs": 30000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 0,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"expectedWidth": 320,
|
||||
"expectedHeight": 180,
|
||||
"fps": 15,
|
||||
"minFpsRatio": 0,
|
||||
"maxFrameGapMs": 0,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": false,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": true,
|
||||
"secondaryPublisherScreenSimulcast": true,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": false,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-smoke/livekit-harness.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 320x180",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": null,
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 72,
|
||||
"inboundVideoFrames": 5,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 0,
|
||||
"videoCallbacks": 5,
|
||||
"videoBytes": 432000,
|
||||
"receivedFps": 0,
|
||||
"requiredFps": null,
|
||||
"maxVideoFrameGapMs": 0,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 132,
|
||||
"maxAudioFrameGapOverallMs": 101,
|
||||
"avDriftMs": 2,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 15,
|
||||
"startedAtMs": 1780978189023,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 0,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 0,
|
||||
"lastPushAtMs": null,
|
||||
"elapsedMs": 1264,
|
||||
"producedFps": 0
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": null,
|
||||
"error": null,
|
||||
"participants": []
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_tmqNx963FdVV",
|
||||
"trackSid": "TR_VS7wQpyDuhX3NX",
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"timestampUs": 524374518000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VS7wQpyDuhX3NX",
|
||||
"source": "screen_share",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 0,
|
||||
"packetsLost": 0,
|
||||
"fps": 10,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"sourceWidth": 320,
|
||||
"sourceHeight": 180,
|
||||
"targetBitrateKbps": 400,
|
||||
"configuredFps": 15,
|
||||
"targetFps": 15,
|
||||
"effectiveFps": 0,
|
||||
"framesProduced": 0,
|
||||
"framesAccepted": 0,
|
||||
"framesDropped": 0,
|
||||
"framesCoalesced": 0,
|
||||
"framesCaptured": 0,
|
||||
"captureFailures": 0,
|
||||
"maxQueueAgeMs": 0,
|
||||
"maxPushLatencyMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable",
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMq6BwJ6fu5yXx",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 0,
|
||||
"packetsLost": 0,
|
||||
"audioLevel": 0.305,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoQueueCapacity": 8,
|
||||
"outgoingVideoMaxQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoPacingTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingVideoPacingMode": "source",
|
||||
"outgoingVideoBusActive": false,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 23,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 2,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_tmqNx963FdVV",
|
||||
"trackSid": "TR_AMq6BwJ6fu5yXx",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 0,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_tmqNx963FdVV",
|
||||
"trackSid": "TR_VS7wQpyDuhX3NX",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 0,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 0,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoQueueCapacity": 0,
|
||||
"outgoingVideoMaxQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoPacingTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingVideoPacingMode": "idle",
|
||||
"outgoingVideoBusActive": false,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,334 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-09T04:15:05.489Z",
|
||||
"endedAt": "2026-06-09T04:15:36.366Z",
|
||||
"platform": "linux",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://10.211.55.2:7880",
|
||||
"serverApiUrl": "http://10.211.55.2:7880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-283674-883784d5-5c9a-4466-84b5-dade644ebf82",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-283674",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-283674",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-283674",
|
||||
"required": true,
|
||||
"strict": false,
|
||||
"timeoutMs": 30000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 0,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"expectedWidth": 320,
|
||||
"expectedHeight": 180,
|
||||
"fps": 15,
|
||||
"minFpsRatio": 0,
|
||||
"maxFrameGapMs": 0,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": false,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": true,
|
||||
"secondaryPublisherScreenSimulcast": true,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": false,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/media/psf/fluxer-dev/fluxer_desktop/native-media-reports/2026-06-09T0413-codex-fedora-livekit-smoke.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 320x180",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "Error: timed out after 30000ms waiting for: publisher local screenshare publication, subscriber remote screenshare subscription, publisher outbound screenshare stats, publisher outbound screenshare codec video/VP8\n at waitForHarnessSuccess (file:///media/psf/fluxer-dev/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1232:8)\n at async runHarness (file:///media/psf/fluxer-dev/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1831:18)\n at async main (file:///media/psf/fluxer-dev/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1931:18)\n at async file:///media/psf/fluxer-dev/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1964:19",
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "runtime_prerequisite_missing",
|
||||
"detail": "missing libcuda.so.1, libnvidia-encode.so.1"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 2994,
|
||||
"inboundVideoFrames": 300,
|
||||
"strictAudioFrames": 297,
|
||||
"strictVideoFrames": 300,
|
||||
"videoCallbacks": 300,
|
||||
"videoBytes": 25920000,
|
||||
"receivedFps": 10,
|
||||
"requiredFps": null,
|
||||
"maxVideoFrameGapMs": 120,
|
||||
"maxAudioFrameGapMs": 106,
|
||||
"maxVideoFrameGapOverallMs": 120,
|
||||
"maxAudioFrameGapOverallMs": 106,
|
||||
"avDriftMs": 4,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"320x180": 300
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 15,
|
||||
"startedAtMs": 1780978506249,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 0,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 0,
|
||||
"lastPushAtMs": null,
|
||||
"elapsedMs": 30121,
|
||||
"producedFps": 0
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": null,
|
||||
"error": null,
|
||||
"participants": []
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_XodNLgkfL4bV",
|
||||
"trackSid": "TR_VSVvE7VrQKNqwD",
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"timestampUs": 124248690000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 5,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AM3ooPAYg5Q2M6",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.8,
|
||||
"packetsLost": 0,
|
||||
"audioLevel": 0.305,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSVvE7VrQKNqwD",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 2.6,
|
||||
"packetsLost": 0,
|
||||
"fps": 9,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"sourceWidth": 320,
|
||||
"sourceHeight": 180,
|
||||
"targetBitrateKbps": 400,
|
||||
"configuredFps": 15,
|
||||
"targetFps": 15,
|
||||
"effectiveFps": 0,
|
||||
"framesProduced": 0,
|
||||
"framesAccepted": 0,
|
||||
"framesDropped": 0,
|
||||
"framesCoalesced": 0,
|
||||
"framesCaptured": 0,
|
||||
"captureFailures": 0,
|
||||
"maxQueueAgeMs": 0,
|
||||
"maxPushLatencyMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable",
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoQueueCapacity": 8,
|
||||
"outgoingVideoMaxQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoPacingTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingVideoPacingMode": "source",
|
||||
"outgoingVideoBusActive": false,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 27,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 5,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_XodNLgkfL4bV",
|
||||
"trackSid": "TR_AM3ooPAYg5Q2M6",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99.1,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_XodNLgkfL4bV",
|
||||
"trackSid": "TR_VSVvE7VrQKNqwD",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 2.6,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoQueueCapacity": 0,
|
||||
"outgoingVideoMaxQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoPacingTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingVideoPacingMode": "idle",
|
||||
"outgoingVideoBusActive": false,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
-287
@@ -1,287 +0,0 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"startedAt": "2026-06-05T20:50:00.568Z",
|
||||
"endedAt": "2026-06-05T20:50:17.383Z",
|
||||
"platform": "linux",
|
||||
"arch": "x64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-92677-28ffd7a4-19ee-49cf-87d1-e31ab80864f4",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-92677",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-92677",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-92677",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 90000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 15000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.9,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["h264"],
|
||||
"expectedScreenCodecs": ["h264"],
|
||||
"codec": "h264",
|
||||
"expectedScreenCodec": "video/H264",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "h264",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/H264",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": false,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": false,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/home/hampus/Development/fluxer/fluxer_desktop/native-media-reports/cachyos-nvidia-livekit-720p-h264-20260605T204959Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-h264",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": null,
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "no_supported_devices",
|
||||
"detail": "missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 465,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 435,
|
||||
"videoCallbacks": 463,
|
||||
"videoBytes": 640051200,
|
||||
"receivedFps": 28.99,
|
||||
"requiredFps": 27,
|
||||
"maxVideoFrameGapMs": 68,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 68,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 435
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780692601312,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 483,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 37,
|
||||
"lastPushAtMs": 1780692617378,
|
||||
"elapsedMs": 16227,
|
||||
"producedFps": 29.77
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780692602380,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-92677",
|
||||
"trackCount": 1,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VStBVT57Q9oKAa",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/H264",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-92677",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_89hNCmrznA6A",
|
||||
"trackSid": "TR_VStBVT57Q9oKAa",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 68521962000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 4,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VStBVT57Q9oKAa",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 386.9,
|
||||
"packetsLost": 0,
|
||||
"fps": 30,
|
||||
"codec": "video/H264"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 476,
|
||||
"outgoingVideoFramesAccepted": 476,
|
||||
"outgoingVideoFramesDropped": 16,
|
||||
"outgoingVideoFramesCoalesced": 16,
|
||||
"outgoingVideoFramesCaptured": 460,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.97,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 34,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 5,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_89hNCmrznA6A",
|
||||
"trackSid": "TR_VStBVT57Q9oKAa",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 386.4,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"codec": "video/H264"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-287
@@ -1,287 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T20:50:29.534Z",
|
||||
"endedAt": "2026-06-05T20:52:00.423Z",
|
||||
"platform": "linux",
|
||||
"arch": "x64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-92838-08153292-483f-4acb-b470-af9847d7597c",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-92838",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-92838",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-92838",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 90000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 15000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.9,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["hevc"],
|
||||
"expectedScreenCodecs": ["hevc"],
|
||||
"codec": "hevc",
|
||||
"expectedScreenCodec": "video/H265",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "hevc",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/H265",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": false,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": false,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/home/hampus/Development/fluxer/fluxer_desktop/native-media-reports/cachyos-nvidia-livekit-720p-hevc-20260605T205028Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-hevc",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/H265",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/H265",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": false
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "Error: timed out after 90000ms waiting for: publisher outbound screenshare codec video/H265, subscriber inbound video codec video/H265, server sees publisher screenshare publication\n at waitForHarnessSuccess (file:///home/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1329:8)\n at async runHarness (file:///home/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1926:18)\n at async main (file:///home/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:2026:18)\n at async file:///home/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:2059:19",
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "no_supported_devices",
|
||||
"detail": "missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 2611,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 2610,
|
||||
"videoCallbacks": 2610,
|
||||
"videoBytes": 3608064000,
|
||||
"receivedFps": 29.02,
|
||||
"requiredFps": 27,
|
||||
"maxVideoFrameGapMs": 81,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 81,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 2610
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780692630259,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 2705,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 37,
|
||||
"lastPushAtMs": 1780692720392,
|
||||
"elapsedMs": 90321,
|
||||
"producedFps": 29.95
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780692720171,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-92838",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-92838",
|
||||
"trackCount": 1,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSr7CavPmttCck",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_eQVNaBPkxUPX",
|
||||
"trackSid": "TR_VSr7CavPmttCck",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 68625011000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 6,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VSr7CavPmttCck",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 44.7,
|
||||
"packetsLost": 0,
|
||||
"fps": 29,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 2697,
|
||||
"outgoingVideoFramesAccepted": 2697,
|
||||
"outgoingVideoFramesDropped": 91,
|
||||
"outgoingVideoFramesCoalesced": 91,
|
||||
"outgoingVideoFramesCaptured": 2605,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.98,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 34,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 6,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_eQVNaBPkxUPX",
|
||||
"trackSid": "TR_VSr7CavPmttCck",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 43.1,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-226
@@ -1,226 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T20:56:44.980Z",
|
||||
"endedAt": "2026-06-05T20:56:45.769Z",
|
||||
"platform": "linux",
|
||||
"arch": "x64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-94317-ce85bdff-9476-465a-9f9a-fae49f6c168d",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-94317",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-94317",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-94317",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 90000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 15000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.9,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["hevc"],
|
||||
"expectedScreenCodecs": ["hevc"],
|
||||
"codec": "hevc",
|
||||
"expectedScreenCodec": "video/H265",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "hevc",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/H265",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": false,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": false,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/home/hampus/Development/fluxer/fluxer_desktop/native-media-reports/cachyos-nvidia-livekit-720p-hevc-failfast-20260605T205643Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-hevc",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/H265",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/H265",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": false
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "Error: H.265 publishing requires hardware encoder support; missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))",
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "no_supported_devices",
|
||||
"detail": "missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 0,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 0,
|
||||
"videoCallbacks": 0,
|
||||
"videoBytes": 0,
|
||||
"receivedFps": 0,
|
||||
"requiredFps": 27,
|
||||
"maxVideoFrameGapMs": 0,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 0,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": null,
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": null,
|
||||
"error": null,
|
||||
"participants": []
|
||||
},
|
||||
"lastVideoMeta": null,
|
||||
"publisherStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-637
@@ -1,637 +0,0 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"startedAt": "2026-06-05T20:57:06.276Z",
|
||||
"endedAt": "2026-06-05T20:57:28.255Z",
|
||||
"platform": "linux",
|
||||
"arch": "x64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-94553-a59031d7-277a-4d31-a290-59cea2c79f1c",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-94553",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-94553",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-94553",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 90000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 20000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.9,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 150,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["h264"],
|
||||
"expectedScreenCodecs": ["h264"],
|
||||
"codec": "h264",
|
||||
"expectedScreenCodec": "video/H264",
|
||||
"secondaryPublisher": true,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": true,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": true,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/home/hampus/Development/fluxer/fluxer_desktop/native-media-reports/cachyos-nvidia-livekit-alltracks-serverpub-mixed-20260605T205705Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-h264-with-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 4",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound secondary codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber secondary data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": null,
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "no_supported_devices",
|
||||
"detail": "missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 8352,
|
||||
"inboundVideoFrames": 1182,
|
||||
"strictAudioFrames": 200,
|
||||
"strictVideoFrames": 1164,
|
||||
"videoCallbacks": 1180,
|
||||
"videoBytes": 1631232000,
|
||||
"receivedFps": 58.16,
|
||||
"requiredFps": 27,
|
||||
"maxVideoFrameGapMs": 98,
|
||||
"maxAudioFrameGapMs": 101,
|
||||
"maxVideoFrameGapOverallMs": 98,
|
||||
"maxAudioFrameGapOverallMs": 101,
|
||||
"avDriftMs": 36,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 1164
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780693027399,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 626,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 39,
|
||||
"lastPushAtMs": 1780693048233,
|
||||
"elapsedMs": 21015,
|
||||
"producedFps": 29.79
|
||||
},
|
||||
"secondaryPublisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780693027400,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 626,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 40,
|
||||
"lastPushAtMs": 1780693048233,
|
||||
"elapsedMs": 21014,
|
||||
"producedFps": 29.79
|
||||
},
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780693027997,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-94553",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_AsXuPkpunfLw9E",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_VStW6iBWUPJvYp",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/H264",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMraWSQbYVLoud",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-94553",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-2-94553",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSDUf6mLBmkksq",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMuo9vvgBhxrxp",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_Asf2caReF2dUoe",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_sFVVyQMPfG8Z",
|
||||
"trackSid": "TR_VSDUf6mLBmkksq",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 68952853000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 15,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VStW6iBWUPJvYp",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 399.8,
|
||||
"packetsLost": 0,
|
||||
"fps": 29,
|
||||
"codec": "video/H264"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AsXuPkpunfLw9E",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 258.9,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMraWSQbYVLoud",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 97.8,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_sFVVyQMPfG8Z",
|
||||
"trackSid": "TR_AMuo9vvgBhxrxp",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 104.8,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 3,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_sFVVyQMPfG8Z",
|
||||
"trackSid": "TR_Asf2caReF2dUoe",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 274.6,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_sFVVyQMPfG8Z",
|
||||
"trackSid": "TR_VSDUf6mLBmkksq",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 31.6,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 5,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 617,
|
||||
"outgoingVideoFramesAccepted": 617,
|
||||
"outgoingVideoFramesDropped": 19,
|
||||
"outgoingVideoFramesCoalesced": 19,
|
||||
"outgoingVideoFramesCaptured": 598,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 29.05,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 33,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 30,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": {
|
||||
"rttMs": 38,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_Asf2caReF2dUoe",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 261.7,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSDUf6mLBmkksq",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 29.4,
|
||||
"packetsLost": 0,
|
||||
"fps": 29,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMuo9vvgBhxrxp",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.8,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_mQQgkdVbS4Wi",
|
||||
"trackSid": "TR_AsXuPkpunfLw9E",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 265.5,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 3,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_mQQgkdVbS4Wi",
|
||||
"trackSid": "TR_VStW6iBWUPJvYp",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 402.2,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 5,
|
||||
"codec": "video/H264"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_mQQgkdVbS4Wi",
|
||||
"trackSid": "TR_AMraWSQbYVLoud",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 100.3,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 618,
|
||||
"outgoingVideoFramesAccepted": 618,
|
||||
"outgoingVideoFramesDropped": 19,
|
||||
"outgoingVideoFramesCoalesced": 19,
|
||||
"outgoingVideoFramesCaptured": 599,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 29.07,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 33,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 30,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"subscriberStats": {
|
||||
"rttMs": 6,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_sFVVyQMPfG8Z",
|
||||
"trackSid": "TR_AMuo9vvgBhxrxp",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 100.8,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 3,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_sFVVyQMPfG8Z",
|
||||
"trackSid": "TR_VSDUf6mLBmkksq",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 29.4,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 5,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_sFVVyQMPfG8Z",
|
||||
"trackSid": "TR_Asf2caReF2dUoe",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 271.7,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_mQQgkdVbS4Wi",
|
||||
"trackSid": "TR_VStW6iBWUPJvYp",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 399.8,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 5,
|
||||
"codec": "video/H264"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_mQQgkdVbS4Wi",
|
||||
"trackSid": "TR_AMraWSQbYVLoud",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99.8,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_mQQgkdVbS4Wi",
|
||||
"trackSid": "TR_AsXuPkpunfLw9E",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 263.9,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 3,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-290
@@ -1,290 +0,0 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"startedAt": "2026-06-05T20:41:48.766Z",
|
||||
"endedAt": "2026-06-05T20:42:05.685Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:17880",
|
||||
"serverApiUrl": "http://127.0.0.1:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-99979-bebc8448-fca8-4318-84fb-debd59234bd3",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-99979",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-99979",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-99979",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 90000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 15000,
|
||||
"width": 3840,
|
||||
"height": 2160,
|
||||
"expectedWidth": 3840,
|
||||
"expectedHeight": 2160,
|
||||
"fps": 60,
|
||||
"minFpsRatio": 0.8,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["h264"],
|
||||
"expectedScreenCodecs": ["h264"],
|
||||
"codec": "h264",
|
||||
"expectedScreenCodec": "video/H264",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "h264",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/H264",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": false,
|
||||
"minVideoFps": 24,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": false,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 80000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": false,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": "videotoolbox",
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h264-noadaptive-20260605T204148Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-h264",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 3840x2160",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "hardware encoder backend videotoolbox",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": null,
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 787,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 783,
|
||||
"videoCallbacks": 786,
|
||||
"videoBytes": 9779097600,
|
||||
"receivedFps": 52.46,
|
||||
"requiredFps": 48,
|
||||
"maxVideoFrameGapMs": 49,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 313,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 12,
|
||||
"maxObservedPacketLossDelta": 11,
|
||||
"videoResolutionCounts": {
|
||||
"3840x2160": 783
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 60,
|
||||
"startedAtMs": 1780692109289,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 982,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 44,
|
||||
"lastPushAtMs": 1780692125674,
|
||||
"elapsedMs": 16404,
|
||||
"producedFps": 59.86
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780692110403,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-99979",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-99979",
|
||||
"trackCount": 1,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VShAhgz9DukUav",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/H264",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_7vLduTJN79oF",
|
||||
"trackSid": "TR_VShAhgz9DukUav",
|
||||
"width": 3840,
|
||||
"height": 2160,
|
||||
"timestampUs": 246972075000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VShAhgz9DukUav",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 226,
|
||||
"packetsLost": 0,
|
||||
"fps": 53,
|
||||
"codec": "video/H264"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 943,
|
||||
"outgoingVideoFramesAccepted": 943,
|
||||
"outgoingVideoFramesDropped": 80,
|
||||
"outgoingVideoFramesCoalesced": 80,
|
||||
"outgoingVideoFramesCaptured": 862,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 54.78,
|
||||
"outgoingVideoTargetFps": 60,
|
||||
"outgoingVideoMaxQueueAgeMs": 18,
|
||||
"outgoingVideoMaxPushLatencyMs": 18,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "adaptiveDisabled"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_7vLduTJN79oF",
|
||||
"trackSid": "TR_VShAhgz9DukUav",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 226.1,
|
||||
"packetsLost": 12,
|
||||
"jitterMs": 4,
|
||||
"codec": "video/H264"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-290
@@ -1,290 +0,0 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"startedAt": "2026-06-05T20:42:57.137Z",
|
||||
"endedAt": "2026-06-05T20:43:14.930Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:17880",
|
||||
"serverApiUrl": "http://127.0.0.1:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-215-d2420544-1d14-499d-b7ed-39246026eeba",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-215",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-215",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-215",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 90000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 15000,
|
||||
"width": 3840,
|
||||
"height": 2160,
|
||||
"expectedWidth": 3840,
|
||||
"expectedHeight": 2160,
|
||||
"fps": 60,
|
||||
"minFpsRatio": 0.8,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["h264"],
|
||||
"expectedScreenCodecs": ["h264"],
|
||||
"codec": "h264",
|
||||
"expectedScreenCodec": "video/H264",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "h264",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/H264",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": false,
|
||||
"minVideoFps": 24,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": false,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 80000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": false,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": "videotoolbox",
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h264-restarted-20260605T204256Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-h264",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 3840x2160",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "hardware encoder backend videotoolbox",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": null,
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 835,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 780,
|
||||
"videoCallbacks": 835,
|
||||
"videoBytes": 10388736000,
|
||||
"receivedFps": 51.9,
|
||||
"requiredFps": 48,
|
||||
"maxVideoFrameGapMs": 103,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 133,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 8,
|
||||
"maxObservedPacketLossDelta": 2,
|
||||
"videoResolutionCounts": {
|
||||
"3840x2160": 780
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 60,
|
||||
"startedAtMs": 1780692177759,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 1029,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 47,
|
||||
"lastPushAtMs": 1780692194929,
|
||||
"elapsedMs": 17180,
|
||||
"producedFps": 59.9
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780692179914,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-215",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-215",
|
||||
"trackCount": 1,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSt23d8AR7uRMx",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/H264",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_ywoDZo9MfjAs",
|
||||
"trackSid": "TR_VSt23d8AR7uRMx",
|
||||
"width": 3840,
|
||||
"height": 2160,
|
||||
"timestampUs": 247041331000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VSt23d8AR7uRMx",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 222,
|
||||
"packetsLost": 0,
|
||||
"fps": 52,
|
||||
"codec": "video/H264"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 1000,
|
||||
"outgoingVideoFramesAccepted": 1000,
|
||||
"outgoingVideoFramesDropped": 87,
|
||||
"outgoingVideoFramesCoalesced": 87,
|
||||
"outgoingVideoFramesCaptured": 912,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 54.65,
|
||||
"outgoingVideoTargetFps": 60,
|
||||
"outgoingVideoMaxQueueAgeMs": 18,
|
||||
"outgoingVideoMaxPushLatencyMs": 19,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "adaptiveDisabled"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_ywoDZo9MfjAs",
|
||||
"trackSid": "TR_VSt23d8AR7uRMx",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 221.7,
|
||||
"packetsLost": 8,
|
||||
"jitterMs": 4,
|
||||
"codec": "video/H264"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-290
@@ -1,290 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T20:38:35.984Z",
|
||||
"endedAt": "2026-06-05T20:39:09.401Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:17880",
|
||||
"serverApiUrl": "http://127.0.0.1:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-99305-9b3ba47d-c306-44e8-93be-2f3f219d81d3",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-99305",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-99305",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-99305",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 90000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 30000,
|
||||
"width": 3840,
|
||||
"height": 2160,
|
||||
"expectedWidth": 3840,
|
||||
"expectedHeight": 2160,
|
||||
"fps": 60,
|
||||
"minFpsRatio": 0.8,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["hevc"],
|
||||
"expectedScreenCodecs": ["hevc"],
|
||||
"codec": "hevc",
|
||||
"expectedScreenCodec": "video/H265",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "hevc",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/H265",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 24,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": false,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 80000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": false,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": "videotoolbox",
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h265-20260605T203835Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-hevc",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 3840x2160",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/H265",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/H265",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "hardware encoder backend videotoolbox",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "received FPS 29.9 below 48",
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 911,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 898,
|
||||
"videoCallbacks": 911,
|
||||
"videoBytes": 11334297600,
|
||||
"receivedFps": 29.9,
|
||||
"requiredFps": 48,
|
||||
"maxVideoFrameGapMs": 124,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 124,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 4,
|
||||
"maxObservedPacketLossDelta": 4,
|
||||
"videoResolutionCounts": {
|
||||
"3840x2160": 898
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 60,
|
||||
"startedAtMs": 1780691916459,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 1977,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 35,
|
||||
"lastPushAtMs": 1780691949392,
|
||||
"elapsedMs": 32950,
|
||||
"producedFps": 60
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780691919120,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-99305",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-99305",
|
||||
"trackCount": 1,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSVHTFzX6KKP2d",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/H265",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_p6AhRM2ZweHm",
|
||||
"trackSid": "TR_VSVHTFzX6KKP2d",
|
||||
"width": 3840,
|
||||
"height": 2160,
|
||||
"timestampUs": 246795792000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VSVHTFzX6KKP2d",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 80.1,
|
||||
"packetsLost": 0,
|
||||
"fps": 29,
|
||||
"codec": "video/H265"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 1975,
|
||||
"outgoingVideoFramesAccepted": 1975,
|
||||
"outgoingVideoFramesDropped": 906,
|
||||
"outgoingVideoFramesCoalesced": 906,
|
||||
"outgoingVideoFramesCaptured": 1068,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 32.44,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 18,
|
||||
"outgoingVideoMaxPushLatencyMs": 18,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "fps30",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_p6AhRM2ZweHm",
|
||||
"trackSid": "TR_VSVHTFzX6KKP2d",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 83,
|
||||
"packetsLost": 4,
|
||||
"jitterMs": 2,
|
||||
"codec": "video/H265"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-290
@@ -1,290 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T20:40:48.428Z",
|
||||
"endedAt": "2026-06-05T20:41:21.891Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:17880",
|
||||
"serverApiUrl": "http://127.0.0.1:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-99799-9b73c5f8-24ee-46a6-b641-2268cab488bf",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-99799",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-99799",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-99799",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 90000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 30000,
|
||||
"width": 3840,
|
||||
"height": 2160,
|
||||
"expectedWidth": 3840,
|
||||
"expectedHeight": 2160,
|
||||
"fps": 60,
|
||||
"minFpsRatio": 0.8,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["hevc"],
|
||||
"expectedScreenCodecs": ["hevc"],
|
||||
"codec": "hevc",
|
||||
"expectedScreenCodec": "video/H265",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "hevc",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/H265",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": false,
|
||||
"minVideoFps": 24,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": false,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 80000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": false,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": "videotoolbox",
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h265-noadaptive-20260605T204048Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-hevc",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 3840x2160",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/H265",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/H265",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "hardware encoder backend videotoolbox",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "received FPS 47.42 below 48",
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 1439,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 1424,
|
||||
"videoCallbacks": 1438,
|
||||
"videoBytes": 17891020800,
|
||||
"receivedFps": 47.42,
|
||||
"requiredFps": 48,
|
||||
"maxVideoFrameGapMs": 119,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 119,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 4,
|
||||
"maxObservedPacketLossDelta": 4,
|
||||
"videoResolutionCounts": {
|
||||
"3840x2160": 1424
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 60,
|
||||
"startedAtMs": 1780692048955,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 1977,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 27,
|
||||
"lastPushAtMs": 1780692081889,
|
||||
"elapsedMs": 32944,
|
||||
"producedFps": 60.01
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780692051599,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-99799",
|
||||
"trackCount": 1,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VS7whvguuwE7Ep",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/H265",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-99799",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_6qPYCyPtKNMy",
|
||||
"trackSid": "TR_VS7whvguuwE7Ep",
|
||||
"width": 3840,
|
||||
"height": 2160,
|
||||
"timestampUs": 246928279000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VS7whvguuwE7Ep",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 132.9,
|
||||
"packetsLost": 0,
|
||||
"fps": 47,
|
||||
"codec": "video/H265"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 1971,
|
||||
"outgoingVideoFramesAccepted": 1971,
|
||||
"outgoingVideoFramesDropped": 169,
|
||||
"outgoingVideoFramesCoalesced": 169,
|
||||
"outgoingVideoFramesCaptured": 1802,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 54.85,
|
||||
"outgoingVideoTargetFps": 60,
|
||||
"outgoingVideoMaxQueueAgeMs": 18,
|
||||
"outgoingVideoMaxPushLatencyMs": 18,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "adaptiveDisabled"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_6qPYCyPtKNMy",
|
||||
"trackSid": "TR_VS7whvguuwE7Ep",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 135.8,
|
||||
"packetsLost": 4,
|
||||
"jitterMs": 6,
|
||||
"codec": "video/H265"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-290
@@ -1,290 +0,0 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"startedAt": "2026-06-05T20:42:06.035Z",
|
||||
"endedAt": "2026-06-05T20:42:22.648Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:17880",
|
||||
"serverApiUrl": "http://127.0.0.1:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-99996-9f3eea01-c794-44e7-a66d-a39e3b06f591",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-99996",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-99996",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-99996",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 90000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 15000,
|
||||
"width": 3840,
|
||||
"height": 2160,
|
||||
"expectedWidth": 3840,
|
||||
"expectedHeight": 2160,
|
||||
"fps": 60,
|
||||
"minFpsRatio": 0.8,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": false,
|
||||
"minVideoFps": 24,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": false,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 80000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": false,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": "videotoolbox",
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-vp8-noadaptive-20260605T204205Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 3840x2160",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "hardware encoder backend videotoolbox",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": null,
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 835,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 829,
|
||||
"videoCallbacks": 835,
|
||||
"videoBytes": 10388736000,
|
||||
"receivedFps": 55.21,
|
||||
"requiredFps": 48,
|
||||
"maxVideoFrameGapMs": 138,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 138,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 18,
|
||||
"maxObservedPacketLossDelta": 17,
|
||||
"videoResolutionCounts": {
|
||||
"3840x2160": 829
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 60,
|
||||
"startedAtMs": 1780692126456,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 966,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 28,
|
||||
"lastPushAtMs": 1780692142632,
|
||||
"elapsedMs": 16199,
|
||||
"producedFps": 59.63
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780692127628,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-99996",
|
||||
"trackCount": 1,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSgayVkGgydiPJ",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-99996",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_CRYMSnWyNXJJ",
|
||||
"trackSid": "TR_VSgayVkGgydiPJ",
|
||||
"width": 3840,
|
||||
"height": 2160,
|
||||
"timestampUs": 246989049000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VSgayVkGgydiPJ",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 347.3,
|
||||
"packetsLost": 0,
|
||||
"fps": 55,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 952,
|
||||
"outgoingVideoFramesAccepted": 952,
|
||||
"outgoingVideoFramesDropped": 83,
|
||||
"outgoingVideoFramesCoalesced": 83,
|
||||
"outgoingVideoFramesCaptured": 869,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 54.77,
|
||||
"outgoingVideoTargetFps": 60,
|
||||
"outgoingVideoMaxQueueAgeMs": 18,
|
||||
"outgoingVideoMaxPushLatencyMs": 18,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "adaptiveDisabled"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_CRYMSnWyNXJJ",
|
||||
"trackSid": "TR_VSgayVkGgydiPJ",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 347.9,
|
||||
"packetsLost": 18,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-290
@@ -1,290 +0,0 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"startedAt": "2026-06-05T20:57:47.344Z",
|
||||
"endedAt": "2026-06-05T20:58:04.558Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-1177-7a01a114-afdb-422c-ac68-dd6da5106b46",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-1177",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-1177",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-1177",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 90000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 15000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.9,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["hevc"],
|
||||
"expectedScreenCodecs": ["hevc"],
|
||||
"codec": "hevc",
|
||||
"expectedScreenCodec": "video/H265",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "hevc",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/H265",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": false,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": false,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": "videotoolbox",
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-720p-hevc-after-guard-20260605T205747Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-hevc",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/H265",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/H265",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "hardware encoder backend videotoolbox",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": null,
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 424,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 420,
|
||||
"videoCallbacks": 423,
|
||||
"videoBytes": 584755200,
|
||||
"receivedFps": 28.28,
|
||||
"requiredFps": 27,
|
||||
"maxVideoFrameGapMs": 132,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 500,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 420
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780693067890,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 501,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 55,
|
||||
"lastPushAtMs": 1780693084557,
|
||||
"elapsedMs": 16678,
|
||||
"producedFps": 30.04
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780693069535,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-1177",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-1177",
|
||||
"trackCount": 1,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VScM2KL4qqaVMf",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/H265",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_CEoLG8a8qXZr",
|
||||
"trackSid": "TR_VScM2KL4qqaVMf",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 247930927000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VScM2KL4qqaVMf",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 31.2,
|
||||
"packetsLost": 0,
|
||||
"fps": 28,
|
||||
"codec": "video/H265"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 477,
|
||||
"outgoingVideoFramesAccepted": 477,
|
||||
"outgoingVideoFramesDropped": 23,
|
||||
"outgoingVideoFramesCoalesced": 23,
|
||||
"outgoingVideoFramesCaptured": 454,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.53,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 34,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_CEoLG8a8qXZr",
|
||||
"trackSid": "TR_VScM2KL4qqaVMf",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 31.2,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/H265"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-636
@@ -1,636 +0,0 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"startedAt": "2026-06-05T20:43:38.058Z",
|
||||
"endedAt": "2026-06-05T20:44:00.477Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:17880",
|
||||
"serverApiUrl": "http://127.0.0.1:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-287-d177ae47-231a-4e5f-8b33-e21435c1d78c",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-287",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-287",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-287",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 90000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 20000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.9,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 120,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["h264"],
|
||||
"expectedScreenCodecs": ["h264"],
|
||||
"codec": "h264",
|
||||
"expectedScreenCodec": "video/H264",
|
||||
"secondaryPublisher": true,
|
||||
"secondaryPublisherCodec": "h264",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/H264",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-alltracks-serverpub-20260605T204337Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-h264-with-h264",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 4",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound secondary codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber secondary data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": null,
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 8688,
|
||||
"inboundVideoFrames": 1105,
|
||||
"strictAudioFrames": 200,
|
||||
"strictVideoFrames": 1096,
|
||||
"videoCallbacks": 1105,
|
||||
"videoBytes": 1527552000,
|
||||
"receivedFps": 55.07,
|
||||
"requiredFps": 27,
|
||||
"maxVideoFrameGapMs": 137,
|
||||
"maxAudioFrameGapMs": 102,
|
||||
"maxVideoFrameGapOverallMs": 278,
|
||||
"maxAudioFrameGapOverallMs": 102,
|
||||
"avDriftMs": 16,
|
||||
"maxObservedPacketLoss": 193,
|
||||
"maxObservedPacketLossDelta": 181,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 1096
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780692218670,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 655,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 35,
|
||||
"lastPushAtMs": 1780692240471,
|
||||
"elapsedMs": 21825,
|
||||
"producedFps": 30.01
|
||||
},
|
||||
"secondaryPublisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780692218671,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 655,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 35,
|
||||
"lastPushAtMs": 1780692240471,
|
||||
"elapsedMs": 21824,
|
||||
"producedFps": 30.01
|
||||
},
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780692220222,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-287",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_AMQmi9tr3ijwD7",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsVx6mXCeyrqVB",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_VSjjPS7DRgdqSi",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/H264",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-287",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-2-287",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSVinYCWjRrXgb",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/H264",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMd395vNaQvHas",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsSinL94mqRm57",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_cdeic4ojSyhV",
|
||||
"trackSid": "TR_VSVinYCWjRrXgb",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 247086842000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AsVx6mXCeyrqVB",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 263.9,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMQmi9tr3ijwD7",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.7,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSjjPS7DRgdqSi",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 38.3,
|
||||
"packetsLost": 0,
|
||||
"fps": 28,
|
||||
"codec": "video/H264"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_cdeic4ojSyhV",
|
||||
"trackSid": "TR_AsSinL94mqRm57",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 242.9,
|
||||
"packetsLost": 78,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.319,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_cdeic4ojSyhV",
|
||||
"trackSid": "TR_VSVinYCWjRrXgb",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 39,
|
||||
"packetsLost": 15,
|
||||
"jitterMs": 2,
|
||||
"codec": "video/H264"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_cdeic4ojSyhV",
|
||||
"trackSid": "TR_AMd395vNaQvHas",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 92.8,
|
||||
"packetsLost": 86,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 651,
|
||||
"outgoingVideoFramesAccepted": 651,
|
||||
"outgoingVideoFramesDropped": 27,
|
||||
"outgoingVideoFramesCoalesced": 27,
|
||||
"outgoingVideoFramesCaptured": 623,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.71,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 34,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 32,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AsSinL94mqRm57",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 257.9,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMd395vNaQvHas",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 97.6,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSVinYCWjRrXgb",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 40.3,
|
||||
"packetsLost": 0,
|
||||
"fps": 29,
|
||||
"codec": "video/H264"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_ecrQddbJEyhw",
|
||||
"trackSid": "TR_AsVx6mXCeyrqVB",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 233.5,
|
||||
"packetsLost": 94,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_ecrQddbJEyhw",
|
||||
"trackSid": "TR_VSjjPS7DRgdqSi",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 39.6,
|
||||
"packetsLost": 13,
|
||||
"jitterMs": 2,
|
||||
"codec": "video/H264"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_ecrQddbJEyhw",
|
||||
"trackSid": "TR_AMQmi9tr3ijwD7",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99.1,
|
||||
"packetsLost": 86,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 653,
|
||||
"outgoingVideoFramesAccepted": 653,
|
||||
"outgoingVideoFramesDropped": 27,
|
||||
"outgoingVideoFramesCoalesced": 27,
|
||||
"outgoingVideoFramesCaptured": 625,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.71,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 34,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 32,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"subscriberStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_cdeic4ojSyhV",
|
||||
"trackSid": "TR_AMd395vNaQvHas",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.3,
|
||||
"packetsLost": 33,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.306,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_cdeic4ojSyhV",
|
||||
"trackSid": "TR_VSVinYCWjRrXgb",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 39,
|
||||
"packetsLost": 42,
|
||||
"jitterMs": 2,
|
||||
"codec": "video/H264"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_cdeic4ojSyhV",
|
||||
"trackSid": "TR_AsSinL94mqRm57",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 253.1,
|
||||
"packetsLost": 2,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.319,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_ecrQddbJEyhw",
|
||||
"trackSid": "TR_AsVx6mXCeyrqVB",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 253.3,
|
||||
"packetsLost": 19,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_ecrQddbJEyhw",
|
||||
"trackSid": "TR_AMQmi9tr3ijwD7",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.3,
|
||||
"packetsLost": 3,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_ecrQddbJEyhw",
|
||||
"trackSid": "TR_VSjjPS7DRgdqSi",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 38.4,
|
||||
"packetsLost": 43,
|
||||
"jitterMs": 2,
|
||||
"codec": "video/H264"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-636
@@ -1,636 +0,0 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"startedAt": "2026-06-05T20:45:05.208Z",
|
||||
"endedAt": "2026-06-05T20:45:28.695Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:17880",
|
||||
"serverApiUrl": "http://127.0.0.1:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-421-04fac24c-55a5-4189-9d7a-5689875cf9b9",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-421",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-421",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-421",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 90000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 20000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.9,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 120,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["h264"],
|
||||
"expectedScreenCodecs": ["h264"],
|
||||
"codec": "h264",
|
||||
"expectedScreenCodec": "video/H264",
|
||||
"secondaryPublisher": true,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": true,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": true,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-alltracks-serverpub-mixed-20260605T204504Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-h264-with-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 4",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound secondary codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber secondary data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": null,
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 9080,
|
||||
"inboundVideoFrames": 1223,
|
||||
"strictAudioFrames": 199,
|
||||
"strictVideoFrames": 1137,
|
||||
"videoCallbacks": 1222,
|
||||
"videoBytes": 1689292800,
|
||||
"receivedFps": 56.81,
|
||||
"requiredFps": 27,
|
||||
"maxVideoFrameGapMs": 99,
|
||||
"maxAudioFrameGapMs": 102,
|
||||
"maxVideoFrameGapOverallMs": 100,
|
||||
"maxAudioFrameGapOverallMs": 102,
|
||||
"avDriftMs": 92,
|
||||
"maxObservedPacketLoss": 186,
|
||||
"maxObservedPacketLossDelta": 163,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 1137
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780692305878,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 685,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 36,
|
||||
"lastPushAtMs": 1780692328677,
|
||||
"elapsedMs": 22834,
|
||||
"producedFps": 30
|
||||
},
|
||||
"secondaryPublisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780692305878,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 685,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 36,
|
||||
"lastPushAtMs": 1780692328677,
|
||||
"elapsedMs": 22834,
|
||||
"producedFps": 30
|
||||
},
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780692308436,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-421",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VS73hbjzW6G7kV",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/H264",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMncny8z7koEGm",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_Aso75dfBWxWEyj",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-2-421",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_AsxFsT7RDTdKQu",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_VSUZjTy2hrcZCc",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMuYbUnWUcAZxj",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-421",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_J5CFmBPpkkyM",
|
||||
"trackSid": "TR_VS73hbjzW6G7kV",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 247175096000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VS73hbjzW6G7kV",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 38.6,
|
||||
"packetsLost": 0,
|
||||
"fps": 28,
|
||||
"codec": "video/H264"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_Aso75dfBWxWEyj",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 261.1,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMncny8z7koEGm",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.3,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_9zvEJKeZq55z",
|
||||
"trackSid": "TR_VSUZjTy2hrcZCc",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 29.7,
|
||||
"packetsLost": 37,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_9zvEJKeZq55z",
|
||||
"trackSid": "TR_AsxFsT7RDTdKQu",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 249.7,
|
||||
"packetsLost": 87,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_9zvEJKeZq55z",
|
||||
"trackSid": "TR_AMuYbUnWUcAZxj",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 91.6,
|
||||
"packetsLost": 62,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.321,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 683,
|
||||
"outgoingVideoFramesAccepted": 683,
|
||||
"outgoingVideoFramesDropped": 29,
|
||||
"outgoingVideoFramesCoalesced": 29,
|
||||
"outgoingVideoFramesCaptured": 654,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.71,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 34,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 30,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AMuYbUnWUcAZxj",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99.2,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AsxFsT7RDTdKQu",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 264.8,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSUZjTy2hrcZCc",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 29.7,
|
||||
"packetsLost": 0,
|
||||
"fps": 29,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_J5CFmBPpkkyM",
|
||||
"trackSid": "TR_VS73hbjzW6G7kV",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 38.6,
|
||||
"packetsLost": 1,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/H264"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_J5CFmBPpkkyM",
|
||||
"trackSid": "TR_Aso75dfBWxWEyj",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 260.9,
|
||||
"packetsLost": 4,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.318,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_J5CFmBPpkkyM",
|
||||
"trackSid": "TR_AMncny8z7koEGm",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.3,
|
||||
"packetsLost": 2,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.317,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 683,
|
||||
"outgoingVideoFramesAccepted": 683,
|
||||
"outgoingVideoFramesDropped": 29,
|
||||
"outgoingVideoFramesCoalesced": 29,
|
||||
"outgoingVideoFramesCaptured": 654,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.71,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 34,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 30,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_9zvEJKeZq55z",
|
||||
"trackSid": "TR_VSUZjTy2hrcZCc",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 30.1,
|
||||
"packetsLost": 3,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_9zvEJKeZq55z",
|
||||
"trackSid": "TR_AsxFsT7RDTdKQu",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 259.8,
|
||||
"packetsLost": 3,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.336,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_9zvEJKeZq55z",
|
||||
"trackSid": "TR_AMuYbUnWUcAZxj",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99,
|
||||
"packetsLost": 3,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.352,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_J5CFmBPpkkyM",
|
||||
"trackSid": "TR_AMncny8z7koEGm",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 95.5,
|
||||
"packetsLost": 64,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.306,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_J5CFmBPpkkyM",
|
||||
"trackSid": "TR_VS73hbjzW6G7kV",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 40,
|
||||
"packetsLost": 27,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/H264"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_J5CFmBPpkkyM",
|
||||
"trackSid": "TR_Aso75dfBWxWEyj",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 244.3,
|
||||
"packetsLost": 67,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.31,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-636
@@ -1,636 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T20:37:40.480Z",
|
||||
"endedAt": "2026-06-05T20:37:52.123Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:17880",
|
||||
"serverApiUrl": "http://127.0.0.1:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-99206-d8ecfb6b-5331-4b04-9ac0-0f510f626c7b",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-99206",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-99206",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-99206",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 60000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 10000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.94,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 80,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": true,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 4000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-multipub-audio-nosim-20260605T203740Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8-with-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 4",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound secondary codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber secondary data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "A/V drift 86ms exceeds 80ms; observed packet loss delta 97 exceeds 0",
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 4368,
|
||||
"inboundVideoFrames": 630,
|
||||
"strictAudioFrames": 99,
|
||||
"strictVideoFrames": 575,
|
||||
"videoCallbacks": 630,
|
||||
"videoBytes": 870912000,
|
||||
"receivedFps": 57.46,
|
||||
"requiredFps": 28.2,
|
||||
"maxVideoFrameGapMs": 139,
|
||||
"maxAudioFrameGapMs": 102,
|
||||
"maxVideoFrameGapOverallMs": 139,
|
||||
"maxAudioFrameGapOverallMs": 102,
|
||||
"avDriftMs": 86,
|
||||
"maxObservedPacketLoss": 106,
|
||||
"maxObservedPacketLossDelta": 97,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 575
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780691861125,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 330,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 36,
|
||||
"lastPushAtMs": 1780691872091,
|
||||
"elapsedMs": 11091,
|
||||
"producedFps": 29.75
|
||||
},
|
||||
"secondaryPublisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780691861125,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 330,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 36,
|
||||
"lastPushAtMs": 1780691872091,
|
||||
"elapsedMs": 11091,
|
||||
"producedFps": 29.75
|
||||
},
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780691861869,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-99206",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-99206",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_AsHmDRoRwwH2rj",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_VSFufssZ8C8oGR",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMofk8na2BK2zW",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-2-99206",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSjFwfYRyNg8nM",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMqoCF5pBKLLtZ",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsYeF5v2Lv8BFF",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_mPCx5P6en7uF",
|
||||
"trackSid": "TR_VSjFwfYRyNg8nM",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 246718524000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AMofk8na2BK2zW",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.8,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AsHmDRoRwwH2rj",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 260.4,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSFufssZ8C8oGR",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 27.5,
|
||||
"packetsLost": 0,
|
||||
"fps": 28,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_mPCx5P6en7uF",
|
||||
"trackSid": "TR_AMqoCF5pBKLLtZ",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 94.4,
|
||||
"packetsLost": 36,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.337,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_mPCx5P6en7uF",
|
||||
"trackSid": "TR_VSjFwfYRyNg8nM",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 29,
|
||||
"packetsLost": 20,
|
||||
"jitterMs": 4,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_mPCx5P6en7uF",
|
||||
"trackSid": "TR_AsYeF5v2Lv8BFF",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 252.3,
|
||||
"packetsLost": 36,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.342,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 322,
|
||||
"outgoingVideoFramesAccepted": 322,
|
||||
"outgoingVideoFramesDropped": 14,
|
||||
"outgoingVideoFramesCoalesced": 14,
|
||||
"outgoingVideoFramesCaptured": 308,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.68,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 33,
|
||||
"outgoingVideoMaxPushLatencyMs": 33,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 28,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AsYeF5v2Lv8BFF",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 262.2,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSjFwfYRyNg8nM",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 28.9,
|
||||
"packetsLost": 0,
|
||||
"fps": 28,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMqoCF5pBKLLtZ",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.3,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_ynXQqotkNFtm",
|
||||
"trackSid": "TR_VSFufssZ8C8oGR",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 26.6,
|
||||
"packetsLost": 18,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_ynXQqotkNFtm",
|
||||
"trackSid": "TR_AMofk8na2BK2zW",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.8,
|
||||
"packetsLost": 4,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.323,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_ynXQqotkNFtm",
|
||||
"trackSid": "TR_AsHmDRoRwwH2rj",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 260.6,
|
||||
"packetsLost": 3,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 322,
|
||||
"outgoingVideoFramesAccepted": 322,
|
||||
"outgoingVideoFramesDropped": 14,
|
||||
"outgoingVideoFramesCoalesced": 14,
|
||||
"outgoingVideoFramesCaptured": 308,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.68,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 33,
|
||||
"outgoingVideoMaxPushLatencyMs": 33,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 28,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_mPCx5P6en7uF",
|
||||
"trackSid": "TR_AMqoCF5pBKLLtZ",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 96.1,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_mPCx5P6en7uF",
|
||||
"trackSid": "TR_AsYeF5v2Lv8BFF",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 260.2,
|
||||
"packetsLost": 2,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_mPCx5P6en7uF",
|
||||
"trackSid": "TR_VSjFwfYRyNg8nM",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 29.1,
|
||||
"packetsLost": 7,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_ynXQqotkNFtm",
|
||||
"trackSid": "TR_AMofk8na2BK2zW",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 92.7,
|
||||
"packetsLost": 40,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_ynXQqotkNFtm",
|
||||
"trackSid": "TR_AsHmDRoRwwH2rj",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 231.2,
|
||||
"packetsLost": 45,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.312,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_ynXQqotkNFtm",
|
||||
"trackSid": "TR_VSFufssZ8C8oGR",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 27.8,
|
||||
"packetsLost": 12,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,654 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T20:26:01.950Z",
|
||||
"endedAt": "2026-06-05T20:26:13.710Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-97788-e96dacf0-ab09-478e-8f70-cff476c920c4",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-97788",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-97788",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-97788",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 60000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 10000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 80,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": true,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": true,
|
||||
"secondaryPublisherScreenSimulcast": true,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 4000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-smoke-20260605T202601Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8-with-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 4",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound secondary codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber secondary data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "observed packet loss 104 exceeds 0; observed 2 video frames outside 1280x720",
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 4408,
|
||||
"inboundVideoFrames": 581,
|
||||
"strictAudioFrames": 100,
|
||||
"strictVideoFrames": 554,
|
||||
"videoCallbacks": 579,
|
||||
"videoBytes": 796262400,
|
||||
"receivedFps": 56.03,
|
||||
"requiredFps": 28.5,
|
||||
"maxVideoFrameGapMs": 66,
|
||||
"maxAudioFrameGapMs": 102,
|
||||
"maxVideoFrameGapOverallMs": 128,
|
||||
"maxAudioFrameGapOverallMs": 102,
|
||||
"avDriftMs": 13,
|
||||
"maxObservedPacketLoss": 104,
|
||||
"videoResolutionCounts": {
|
||||
"640x360": 2,
|
||||
"1280x720": 552
|
||||
},
|
||||
"videoResolutionMismatchCount": 2,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780691162656,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 332,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 35,
|
||||
"lastPushAtMs": 1780691173690,
|
||||
"elapsedMs": 11153,
|
||||
"producedFps": 29.77
|
||||
},
|
||||
"secondaryPublisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780691162656,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 332,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 35,
|
||||
"lastPushAtMs": 1780691173690,
|
||||
"elapsedMs": 11153,
|
||||
"producedFps": 29.77
|
||||
},
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780691163699,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-97788",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-2-97788",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_AMYDBcTLVPXW5R",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_VSSvARsPYMjnfH",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AseAEMuwQvUDUq",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-97788",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_AsEpo39LZXJwgU",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMZe28QCXUZLuv",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_VSVNYfqdKkzCqh",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_G5C4xWMJumgR",
|
||||
"trackSid": "TR_VSSvARsPYMjnfH",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 246020100000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VSVNYfqdKkzCqh",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1.9,
|
||||
"packetsLost": 0,
|
||||
"fps": 3,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSVNYfqdKkzCqh",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 35.4,
|
||||
"packetsLost": 0,
|
||||
"fps": 28,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMZe28QCXUZLuv",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.8,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AsEpo39LZXJwgU",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 261.7,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_G5C4xWMJumgR",
|
||||
"trackSid": "TR_VSSvARsPYMjnfH",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 2.3,
|
||||
"packetsLost": 4,
|
||||
"jitterMs": 14,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_G5C4xWMJumgR",
|
||||
"trackSid": "TR_AMYDBcTLVPXW5R",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.2,
|
||||
"packetsLost": 2,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.332,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_G5C4xWMJumgR",
|
||||
"trackSid": "TR_AseAEMuwQvUDUq",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 238,
|
||||
"packetsLost": 44,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.309,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 326,
|
||||
"outgoingVideoFramesAccepted": 326,
|
||||
"outgoingVideoFramesDropped": 16,
|
||||
"outgoingVideoFramesCoalesced": 16,
|
||||
"outgoingVideoFramesCaptured": 309,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.49,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 33,
|
||||
"outgoingVideoMaxPushLatencyMs": 33,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 46,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AseAEMuwQvUDUq",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 257.7,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSSvARsPYMjnfH",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 32.8,
|
||||
"packetsLost": 0,
|
||||
"fps": 28,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSSvARsPYMjnfH",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 2.3,
|
||||
"packetsLost": 0,
|
||||
"fps": 3,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMYDBcTLVPXW5R",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.7,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_t8raa7C57aBi",
|
||||
"trackSid": "TR_AMZe28QCXUZLuv",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 96.9,
|
||||
"packetsLost": 1,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.31,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_t8raa7C57aBi",
|
||||
"trackSid": "TR_VSVNYfqdKkzCqh",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1.9,
|
||||
"packetsLost": 1,
|
||||
"jitterMs": 15,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_t8raa7C57aBi",
|
||||
"trackSid": "TR_AsEpo39LZXJwgU",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 223.8,
|
||||
"packetsLost": 56,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.336,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 323,
|
||||
"outgoingVideoFramesAccepted": 323,
|
||||
"outgoingVideoFramesDropped": 16,
|
||||
"outgoingVideoFramesCoalesced": 16,
|
||||
"outgoingVideoFramesCaptured": 306,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.48,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 33,
|
||||
"outgoingVideoMaxPushLatencyMs": 33,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 47,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_G5C4xWMJumgR",
|
||||
"trackSid": "TR_AseAEMuwQvUDUq",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 258.1,
|
||||
"packetsLost": 1,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_G5C4xWMJumgR",
|
||||
"trackSid": "TR_AMYDBcTLVPXW5R",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 91.1,
|
||||
"packetsLost": 45,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.333,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_G5C4xWMJumgR",
|
||||
"trackSid": "TR_VSSvARsPYMjnfH",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 35,
|
||||
"packetsLost": 4,
|
||||
"jitterMs": 4,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_t8raa7C57aBi",
|
||||
"trackSid": "TR_AsEpo39LZXJwgU",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 261.8,
|
||||
"packetsLost": 2,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.318,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_t8raa7C57aBi",
|
||||
"trackSid": "TR_VSVNYfqdKkzCqh",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 35.4,
|
||||
"packetsLost": 7,
|
||||
"jitterMs": 4,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_t8raa7C57aBi",
|
||||
"trackSid": "TR_AMZe28QCXUZLuv",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 86.9,
|
||||
"packetsLost": 45,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-316
@@ -1,316 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T20:28:48.938Z",
|
||||
"endedAt": "2026-06-05T20:29:18.944Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-98362-5162a4d6-ca28-4b4f-b02e-bea151fc375d",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-98362",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-98362",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-98362",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 60000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 10000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 80,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": true,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": true,
|
||||
"secondaryPublisherScreenSimulcast": true,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 4000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-smoke-autosuboff-20260605T202848Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8-with-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 6",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 2",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 12",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 4",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 12",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher connected",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screenshare publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screenshare subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare codec video/VP8",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound secondary codec video/VP8",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local microphone publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary microphone subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound microphone stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screen-share audio publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screen-share audio subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screen-share audio stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber secondary data packet received",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screenshare publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher microphone publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screen-share audio publication",
|
||||
"pass": false
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "Error: connect timed out after 30000ms\n at file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1251:9\n at async runHarness (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1668:3)\n at async main (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1920:18)\n at async file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1953:19",
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 0,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 0,
|
||||
"videoCallbacks": 0,
|
||||
"videoBytes": 0,
|
||||
"receivedFps": 0,
|
||||
"requiredFps": 28.5,
|
||||
"maxVideoFrameGapMs": 0,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 0,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"videoResolutionCounts": {},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": null,
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": null,
|
||||
"error": null,
|
||||
"participants": []
|
||||
},
|
||||
"lastVideoMeta": null,
|
||||
"publisherStats": null,
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-654
@@ -1,654 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T20:30:06.419Z",
|
||||
"endedAt": "2026-06-05T20:30:18.162Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:17880",
|
||||
"serverApiUrl": "http://127.0.0.1:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-98467-7ef96043-b8a7-42ac-b2b9-d551434e68b6",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-98467",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-98467",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-98467",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 60000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 10000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 80,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": true,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": true,
|
||||
"secondaryPublisherScreenSimulcast": true,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 4000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-smoke-local-20260605T203006Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8-with-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 4",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound secondary codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber secondary data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "observed packet loss 108 exceeds 0; observed 2 video frames outside 1280x720",
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 4400,
|
||||
"inboundVideoFrames": 573,
|
||||
"strictAudioFrames": 99,
|
||||
"strictVideoFrames": 560,
|
||||
"videoCallbacks": 571,
|
||||
"videoBytes": 785203200,
|
||||
"receivedFps": 56.08,
|
||||
"requiredFps": 28.5,
|
||||
"maxVideoFrameGapMs": 103,
|
||||
"maxAudioFrameGapMs": 102,
|
||||
"maxVideoFrameGapOverallMs": 431,
|
||||
"maxAudioFrameGapOverallMs": 114,
|
||||
"avDriftMs": 55,
|
||||
"maxObservedPacketLoss": 108,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 558,
|
||||
"640x360": 2
|
||||
},
|
||||
"videoResolutionMismatchCount": 2,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780691407114,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 332,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 36,
|
||||
"lastPushAtMs": 1780691418147,
|
||||
"elapsedMs": 11141,
|
||||
"producedFps": 29.8
|
||||
},
|
||||
"secondaryPublisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780691407115,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 332,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 36,
|
||||
"lastPushAtMs": 1780691418147,
|
||||
"elapsedMs": 11140,
|
||||
"producedFps": 29.8
|
||||
},
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780691408158,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-98467",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_AMcinu47L8FgLf",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_VS32SFBD3gqCZF",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsHUY22SNjwgPj",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-98467",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-2-98467",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_AMnKcxrhawpPFJ",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_VSmsDZR3M5RREb",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_Asw3rBwsK6H7ob",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_xn87DfYqiNM5",
|
||||
"trackSid": "TR_VSmsDZR3M5RREb",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 246264558000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AsHUY22SNjwgPj",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 261.6,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VS32SFBD3gqCZF",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 2.2,
|
||||
"packetsLost": 0,
|
||||
"fps": 3,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VS32SFBD3gqCZF",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 31.2,
|
||||
"packetsLost": 0,
|
||||
"fps": 28,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMcinu47L8FgLf",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.8,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_xn87DfYqiNM5",
|
||||
"trackSid": "TR_VSmsDZR3M5RREb",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 2.3,
|
||||
"packetsLost": 5,
|
||||
"jitterMs": 13,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_xn87DfYqiNM5",
|
||||
"trackSid": "TR_AMnKcxrhawpPFJ",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.8,
|
||||
"packetsLost": 7,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.313,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_xn87DfYqiNM5",
|
||||
"trackSid": "TR_Asw3rBwsK6H7ob",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 236.5,
|
||||
"packetsLost": 55,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.318,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 326,
|
||||
"outgoingVideoFramesAccepted": 326,
|
||||
"outgoingVideoFramesDropped": 14,
|
||||
"outgoingVideoFramesCoalesced": 14,
|
||||
"outgoingVideoFramesCaptured": 311,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.66,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 34,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 31,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AMnKcxrhawpPFJ",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.6,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSmsDZR3M5RREb",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 33.7,
|
||||
"packetsLost": 0,
|
||||
"fps": 29,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSmsDZR3M5RREb",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 2.3,
|
||||
"packetsLost": 0,
|
||||
"fps": 3,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_Asw3rBwsK6H7ob",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 262.2,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_fFFTp8k4DSa3",
|
||||
"trackSid": "TR_AMcinu47L8FgLf",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 94.8,
|
||||
"packetsLost": 61,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.33,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_fFFTp8k4DSa3",
|
||||
"trackSid": "TR_AsHUY22SNjwgPj",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 252.1,
|
||||
"packetsLost": 44,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.332,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_fFFTp8k4DSa3",
|
||||
"trackSid": "TR_VS32SFBD3gqCZF",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1.2,
|
||||
"packetsLost": 3,
|
||||
"jitterMs": 13,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 319,
|
||||
"outgoingVideoFramesAccepted": 319,
|
||||
"outgoingVideoFramesDropped": 14,
|
||||
"outgoingVideoFramesCoalesced": 14,
|
||||
"outgoingVideoFramesCaptured": 305,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.67,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 34,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 31,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_xn87DfYqiNM5",
|
||||
"trackSid": "TR_AMnKcxrhawpPFJ",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 93,
|
||||
"packetsLost": 42,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.334,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_xn87DfYqiNM5",
|
||||
"trackSid": "TR_VSmsDZR3M5RREb",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 30.7,
|
||||
"packetsLost": 13,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_xn87DfYqiNM5",
|
||||
"trackSid": "TR_Asw3rBwsK6H7ob",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 261.9,
|
||||
"packetsLost": 9,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.318,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_fFFTp8k4DSa3",
|
||||
"trackSid": "TR_AMcinu47L8FgLf",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 94.8,
|
||||
"packetsLost": 13,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.315,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_fFFTp8k4DSa3",
|
||||
"trackSid": "TR_VS32SFBD3gqCZF",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 31.2,
|
||||
"packetsLost": 14,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_fFFTp8k4DSa3",
|
||||
"trackSid": "TR_AsHUY22SNjwgPj",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 261.5,
|
||||
"packetsLost": 3,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.336,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-635
@@ -1,635 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T20:30:42.517Z",
|
||||
"endedAt": "2026-06-05T20:30:54.199Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:17880",
|
||||
"serverApiUrl": "http://127.0.0.1:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-98493-8a9f539a-e21e-481c-99b7-cd5ce35f5b34",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-98493",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-98493",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-98493",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 60000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 10000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 80,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": true,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 4000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-stable-nosimulcast-20260605T203042Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8-with-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 4",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound secondary codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber secondary data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "observed packet loss 240 exceeds 0",
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 4334,
|
||||
"inboundVideoFrames": 602,
|
||||
"strictAudioFrames": 100,
|
||||
"strictVideoFrames": 571,
|
||||
"videoCallbacks": 600,
|
||||
"videoBytes": 829440000,
|
||||
"receivedFps": 57.45,
|
||||
"requiredFps": 28.5,
|
||||
"maxVideoFrameGapMs": 183,
|
||||
"maxAudioFrameGapMs": 102,
|
||||
"maxVideoFrameGapOverallMs": 208,
|
||||
"maxAudioFrameGapOverallMs": 104,
|
||||
"avDriftMs": 42,
|
||||
"maxObservedPacketLoss": 240,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 571
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780691443335,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 326,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 37,
|
||||
"lastPushAtMs": 1780691454168,
|
||||
"elapsedMs": 10954,
|
||||
"producedFps": 29.76
|
||||
},
|
||||
"secondaryPublisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780691443335,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 326,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 36,
|
||||
"lastPushAtMs": 1780691454168,
|
||||
"elapsedMs": 10954,
|
||||
"producedFps": 29.76
|
||||
},
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780691443938,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-2-98493",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VS6eBSydG2Fe6Y",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMwSsNzhnQnZbN",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsYPbyKEiQYdXA",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-98493",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSsm5jzFutA9po",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMd2rFUNTJzRRx",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsJfw9r7g4LU9b",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-98493",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_RjVrb6cfSTYN",
|
||||
"trackSid": "TR_VS6eBSydG2Fe6Y",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 246300548000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VSsm5jzFutA9po",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 28.8,
|
||||
"packetsLost": 0,
|
||||
"fps": 29,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AsJfw9r7g4LU9b",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 258,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMd2rFUNTJzRRx",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.3,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_RjVrb6cfSTYN",
|
||||
"trackSid": "TR_AsYPbyKEiQYdXA",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 257.7,
|
||||
"packetsLost": 2,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_RjVrb6cfSTYN",
|
||||
"trackSid": "TR_VS6eBSydG2Fe6Y",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 27.7,
|
||||
"packetsLost": 8,
|
||||
"jitterMs": 2,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_RjVrb6cfSTYN",
|
||||
"trackSid": "TR_AMwSsNzhnQnZbN",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.1,
|
||||
"packetsLost": 9,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.337,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 319,
|
||||
"outgoingVideoFramesAccepted": 319,
|
||||
"outgoingVideoFramesDropped": 14,
|
||||
"outgoingVideoFramesCoalesced": 14,
|
||||
"outgoingVideoFramesCaptured": 304,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.61,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 35,
|
||||
"outgoingVideoMaxPushLatencyMs": 35,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 30,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AsYPbyKEiQYdXA",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 262.5,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMwSsNzhnQnZbN",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VS6eBSydG2Fe6Y",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 26.6,
|
||||
"packetsLost": 0,
|
||||
"fps": 28,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_SDXDDK8nFR6H",
|
||||
"trackSid": "TR_AMd2rFUNTJzRRx",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.3,
|
||||
"packetsLost": 10,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.333,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_SDXDDK8nFR6H",
|
||||
"trackSid": "TR_AsJfw9r7g4LU9b",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 267.9,
|
||||
"packetsLost": 3,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.336,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_SDXDDK8nFR6H",
|
||||
"trackSid": "TR_VSsm5jzFutA9po",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 27.8,
|
||||
"packetsLost": 7,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 317,
|
||||
"outgoingVideoFramesAccepted": 317,
|
||||
"outgoingVideoFramesDropped": 14,
|
||||
"outgoingVideoFramesCoalesced": 14,
|
||||
"outgoingVideoFramesCaptured": 302,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.61,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 34,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 30,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"subscriberStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_RjVrb6cfSTYN",
|
||||
"trackSid": "TR_AMwSsNzhnQnZbN",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 85.4,
|
||||
"packetsLost": 58,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.316,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_RjVrb6cfSTYN",
|
||||
"trackSid": "TR_AsYPbyKEiQYdXA",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 242.8,
|
||||
"packetsLost": 46,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.383,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_RjVrb6cfSTYN",
|
||||
"trackSid": "TR_VS6eBSydG2Fe6Y",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 26.9,
|
||||
"packetsLost": 26,
|
||||
"jitterMs": 2,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_SDXDDK8nFR6H",
|
||||
"trackSid": "TR_AMd2rFUNTJzRRx",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 94.9,
|
||||
"packetsLost": 52,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.316,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_SDXDDK8nFR6H",
|
||||
"trackSid": "TR_AsJfw9r7g4LU9b",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 258.3,
|
||||
"packetsLost": 42,
|
||||
"jitterMs": 0,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_SDXDDK8nFR6H",
|
||||
"trackSid": "TR_VSsm5jzFutA9po",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 28,
|
||||
"packetsLost": 16,
|
||||
"jitterMs": 2,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-1405
File diff suppressed because it is too large
Load Diff
-285
@@ -1,285 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T20:31:04.019Z",
|
||||
"endedAt": "2026-06-05T20:31:15.493Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:17880",
|
||||
"serverApiUrl": "http://127.0.0.1:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-98534-07955759-bfdc-4fc0-8941-6c23669306cb",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-98534",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-98534",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-98534",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 60000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 10000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": false,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 4000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": false,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-20260605T203103Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "received FPS 19.96 below 28.5; observed packet loss 1 exceeds 0",
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 227,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 200,
|
||||
"videoCallbacks": 226,
|
||||
"videoBytes": 312422400,
|
||||
"receivedFps": 19.96,
|
||||
"requiredFps": 28.5,
|
||||
"maxVideoFrameGapMs": 98,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 107,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 1,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 200
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780691464440,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 332,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 36,
|
||||
"lastPushAtMs": 1780691475476,
|
||||
"elapsedMs": 11142,
|
||||
"producedFps": 29.8
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780691465483,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-98534",
|
||||
"trackCount": 1,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSCdbHfS7Qjnp9",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-98534",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_NeWDVeMforAq",
|
||||
"trackSid": "TR_VSCdbHfS7Qjnp9",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 246321877000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VSCdbHfS7Qjnp9",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 28.5,
|
||||
"packetsLost": 0,
|
||||
"fps": 14,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 330,
|
||||
"outgoingVideoFramesAccepted": 330,
|
||||
"outgoingVideoFramesDropped": 100,
|
||||
"outgoingVideoFramesCoalesced": 100,
|
||||
"outgoingVideoFramesCaptured": 229,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 20.83,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 34,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "fps15",
|
||||
"adaptiveSendReason": "frameCoalescingPressure"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_NeWDVeMforAq",
|
||||
"trackSid": "TR_VSCdbHfS7Qjnp9",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 30.5,
|
||||
"packetsLost": 1,
|
||||
"jitterMs": 4,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-285
@@ -1,285 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T20:35:23.806Z",
|
||||
"endedAt": "2026-06-05T20:35:35.256Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:17880",
|
||||
"serverApiUrl": "http://127.0.0.1:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-99020-a8faf4d3-b9ea-4425-b85e-484c3439bea8",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-99020",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-99020",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-99020",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 60000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 10000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": false,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 4000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": false,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-adaptive-fixed-20260605T203523Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "observed packet loss 4 exceeds 0",
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 314,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 287,
|
||||
"videoCallbacks": 314,
|
||||
"videoBytes": 434073600,
|
||||
"receivedFps": 28.89,
|
||||
"requiredFps": 28.5,
|
||||
"maxVideoFrameGapMs": 65,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 174,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 4,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 287
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780691724194,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 332,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 38,
|
||||
"lastPushAtMs": 1780691735226,
|
||||
"elapsedMs": 11160,
|
||||
"producedFps": 29.75
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780691725246,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-99020",
|
||||
"trackCount": 1,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSNY7hqQXsYjDU",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-99020",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_4XVg8hrTERta",
|
||||
"trackSid": "TR_VSNY7hqQXsYjDU",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 246581659000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VSNY7hqQXsYjDU",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 29.1,
|
||||
"packetsLost": 0,
|
||||
"fps": 28,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 330,
|
||||
"outgoingVideoFramesAccepted": 330,
|
||||
"outgoingVideoFramesDropped": 16,
|
||||
"outgoingVideoFramesCoalesced": 16,
|
||||
"outgoingVideoFramesCaptured": 314,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.49,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 34,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_4XVg8hrTERta",
|
||||
"trackSid": "TR_VSNY7hqQXsYjDU",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 29.1,
|
||||
"packetsLost": 4,
|
||||
"jitterMs": 2,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-285
@@ -1,285 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T20:31:34.574Z",
|
||||
"endedAt": "2026-06-05T20:31:45.998Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:17880",
|
||||
"serverApiUrl": "http://127.0.0.1:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-98569-9a0c5b4a-c12c-4cce-97ba-5481af7273d4",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-98569",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-98569",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-98569",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 60000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 10000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": false,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": false,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 4000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": false,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-noadaptive-20260605T203134Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "received FPS 28.45 below 28.5",
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 312,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 285,
|
||||
"videoCallbacks": 311,
|
||||
"videoBytes": 429926400,
|
||||
"receivedFps": 28.45,
|
||||
"requiredFps": 28.5,
|
||||
"maxVideoFrameGapMs": 61,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 69,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 285
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780691494944,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 332,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 36,
|
||||
"lastPushAtMs": 1780691505978,
|
||||
"elapsedMs": 11144,
|
||||
"producedFps": 29.79
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780691495987,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-98569",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-98569",
|
||||
"trackCount": 1,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSnPVaEqt8wLKt",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_4H7SUk7TRbA7",
|
||||
"trackSid": "TR_VSnPVaEqt8wLKt",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 246352378000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VSnPVaEqt8wLKt",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 28.1,
|
||||
"packetsLost": 0,
|
||||
"fps": 28,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 330,
|
||||
"outgoingVideoFramesAccepted": 330,
|
||||
"outgoingVideoFramesDropped": 17,
|
||||
"outgoingVideoFramesCoalesced": 17,
|
||||
"outgoingVideoFramesCaptured": 313,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.44,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 34,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "adaptiveDisabled"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_4H7SUk7TRbA7",
|
||||
"trackSid": "TR_VSnPVaEqt8wLKt",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 28.1,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-285
@@ -1,285 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T20:32:57.846Z",
|
||||
"endedAt": "2026-06-05T20:33:09.335Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:17880",
|
||||
"serverApiUrl": "http://127.0.0.1:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-98748-d3e8cf0e-bc5c-48e8-b5ef-ed0f993fbe86",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-98748",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-98748",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-98748",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 60000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 10000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": false,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 4000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": false,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-paced-20260605T203257Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "received FPS 20.11 below 28.5; observed packet loss 2 exceeds 0",
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 226,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 201,
|
||||
"videoCallbacks": 225,
|
||||
"videoBytes": 311040000,
|
||||
"receivedFps": 20.11,
|
||||
"requiredFps": 28.5,
|
||||
"maxVideoFrameGapMs": 173,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 173,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 2,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 201
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780691578281,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 332,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 35,
|
||||
"lastPushAtMs": 1780691589316,
|
||||
"elapsedMs": 11143,
|
||||
"producedFps": 29.79
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780691579324,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-98748",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-98748",
|
||||
"trackCount": 1,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VS5AVMD9QiLw9T",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_wGf7jDpSGqVf",
|
||||
"trackSid": "TR_VS5AVMD9QiLw9T",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 246435681000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VS5AVMD9QiLw9T",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 28.5,
|
||||
"packetsLost": 0,
|
||||
"fps": 14,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 330,
|
||||
"outgoingVideoFramesAccepted": 330,
|
||||
"outgoingVideoFramesDropped": 100,
|
||||
"outgoingVideoFramesCoalesced": 100,
|
||||
"outgoingVideoFramesCaptured": 230,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 20.87,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 34,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "fps15",
|
||||
"adaptiveSendReason": "frameCoalescingPressure"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_wGf7jDpSGqVf",
|
||||
"trackSid": "TR_VS5AVMD9QiLw9T",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 28.5,
|
||||
"packetsLost": 2,
|
||||
"jitterMs": 2,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-286
@@ -1,286 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T20:37:10.528Z",
|
||||
"endedAt": "2026-06-05T20:37:21.966Z",
|
||||
"platform": "darwin",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://127.0.0.1:17880",
|
||||
"serverApiUrl": "http://127.0.0.1:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-99097-fadf776d-a03a-42a1-92bc-c45de56c5a2c",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-99097",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-99097",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-99097",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 60000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 10000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "fast",
|
||||
"videoInput": "nv12",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": false,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 4000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": false,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-strict-delta-20260605T203710Z.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "received FPS 28.46 below 28.5",
|
||||
"hardwareEncoder": {
|
||||
"available": true,
|
||||
"backend": "videotoolbox",
|
||||
"compiled": true,
|
||||
"runtime": true,
|
||||
"codecs": ["h264", "h265"],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 310,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 285,
|
||||
"videoCallbacks": 309,
|
||||
"videoBytes": 427161600,
|
||||
"receivedFps": 28.46,
|
||||
"requiredFps": 28.5,
|
||||
"maxVideoFrameGapMs": 63,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 69,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 285
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "nv12",
|
||||
"pattern": "fast",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780691830909,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 332,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 36,
|
||||
"lastPushAtMs": 1780691841942,
|
||||
"elapsedMs": 11151,
|
||||
"producedFps": 29.77
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780691831955,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-99097",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-99097",
|
||||
"trackCount": 1,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSmHHYYxDdRMdt",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_ZYRki8TB8qqy",
|
||||
"trackSid": "TR_VSmHHYYxDdRMdt",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 246688341000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VSmHHYYxDdRMdt",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 28.2,
|
||||
"packetsLost": 0,
|
||||
"fps": 29,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 329,
|
||||
"outgoingVideoFramesAccepted": 329,
|
||||
"outgoingVideoFramesDropped": 16,
|
||||
"outgoingVideoFramesCoalesced": 16,
|
||||
"outgoingVideoFramesCaptured": 313,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.51,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 34,
|
||||
"outgoingVideoMaxPushLatencyMs": 34,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_ZYRki8TB8qqy",
|
||||
"trackSid": "TR_VSmHHYYxDdRMdt",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 27.3,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-391
@@ -1,391 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T22:33:47.023Z",
|
||||
"endedAt": "2026-06-05T22:34:19.284Z",
|
||||
"platform": "win32",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-6084-b067b112-e82c-4f9b-a8e5-8c17b58ce892",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-6084",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-6084",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-6084",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 120000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 30000,
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"expectedWidth": 640,
|
||||
"expectedHeight": 360,
|
||||
"fps": 10,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 150,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["h264"],
|
||||
"expectedScreenCodecs": ["h264"],
|
||||
"codec": "h264",
|
||||
"expectedScreenCodec": "video/H264",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "h264",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/H264",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 10,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 3000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\h264-360p10-strict-single-20260605.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-h264",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 640x360",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "received FPS 9.43 below 9.5",
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "runtime_prerequisite_missing",
|
||||
"detail": "missing nvcuda.dll, nvEncodeAPI64.dll"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 6176,
|
||||
"inboundVideoFrames": 287,
|
||||
"strictAudioFrames": 299,
|
||||
"strictVideoFrames": 283,
|
||||
"videoCallbacks": 287,
|
||||
"videoBytes": 99187200,
|
||||
"receivedFps": 9.43,
|
||||
"requiredFps": 9.5,
|
||||
"maxVideoFrameGapMs": 204,
|
||||
"maxAudioFrameGapMs": 103,
|
||||
"maxVideoFrameGapOverallMs": 204,
|
||||
"maxAudioFrameGapOverallMs": 103,
|
||||
"avDriftMs": 4,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"640x360": 283
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 10,
|
||||
"startedAtMs": 1780698828184,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 311,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 103,
|
||||
"lastPushAtMs": 1780698859185,
|
||||
"elapsedMs": 31105,
|
||||
"producedFps": 10
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780698829266,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-6084",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSTAkE5Tyvf84k",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/H264",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMR8qs636tXCeM",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsiQhtzQxKx3hg",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-6084",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_yWuJtiAsGJHk",
|
||||
"trackSid": "TR_VSTAkE5Tyvf84k",
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"timestampUs": 1737095000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 4,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AsiQhtzQxKx3hg",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 259.9,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMR8qs636tXCeM",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSTAkE5Tyvf84k",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1267,
|
||||
"packetsLost": 0,
|
||||
"fps": 11,
|
||||
"codec": "video/H264"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 310,
|
||||
"outgoingVideoFramesAccepted": 310,
|
||||
"outgoingVideoFramesDropped": 4,
|
||||
"outgoingVideoFramesCoalesced": 4,
|
||||
"outgoingVideoFramesCaptured": 306,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 9.87,
|
||||
"outgoingVideoTargetFps": 10,
|
||||
"outgoingVideoMaxQueueAgeMs": 100,
|
||||
"outgoingVideoMaxPushLatencyMs": 101,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 56,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 4,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_yWuJtiAsGJHk",
|
||||
"trackSid": "TR_AMR8qs636tXCeM",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.354,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_yWuJtiAsGJHk",
|
||||
"trackSid": "TR_VSTAkE5Tyvf84k",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1267,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 4,
|
||||
"codec": "video/H264"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_yWuJtiAsGJHk",
|
||||
"trackSid": "TR_AsiQhtzQxKx3hg",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 259.9,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-391
@@ -1,391 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T22:32:58.250Z",
|
||||
"endedAt": "2026-06-05T22:33:30.493Z",
|
||||
"platform": "win32",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-4444-6d4fc834-c519-4a03-831b-1f3c4f7b8db5",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-4444",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-4444",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-4444",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 120000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 30000,
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"expectedWidth": 640,
|
||||
"expectedHeight": 360,
|
||||
"fps": 15,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 150,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["h264"],
|
||||
"expectedScreenCodecs": ["h264"],
|
||||
"codec": "h264",
|
||||
"expectedScreenCodec": "video/H264",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "h264",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/H264",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 4000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\h264-360p15-strict-single-20260605.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-h264",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 640x360",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "received FPS 11.83 below 14.25",
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "runtime_prerequisite_missing",
|
||||
"detail": "missing nvcuda.dll, nvEncodeAPI64.dll"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 6188,
|
||||
"inboundVideoFrames": 360,
|
||||
"strictAudioFrames": 299,
|
||||
"strictVideoFrames": 356,
|
||||
"videoCallbacks": 360,
|
||||
"videoBytes": 124416000,
|
||||
"receivedFps": 11.83,
|
||||
"requiredFps": 14.25,
|
||||
"maxVideoFrameGapMs": 220,
|
||||
"maxAudioFrameGapMs": 115,
|
||||
"maxVideoFrameGapOverallMs": 220,
|
||||
"maxAudioFrameGapOverallMs": 115,
|
||||
"avDriftMs": 16,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"640x360": 356
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 15,
|
||||
"startedAtMs": 1780698779365,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 467,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 87,
|
||||
"lastPushAtMs": 1780698810433,
|
||||
"elapsedMs": 31131,
|
||||
"producedFps": 15
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780698780443,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-4444",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-4444",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSu5oXdquxv9yn",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/H264",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMw39xhBtq2iKo",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsUfFKczDubKFH",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_jmECWh8cUV8Z",
|
||||
"trackSid": "TR_VSu5oXdquxv9yn",
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"timestampUs": 1688313000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AsUfFKczDubKFH",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 263,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMw39xhBtq2iKo",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 97.9,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSu5oXdquxv9yn",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1957.3,
|
||||
"packetsLost": 0,
|
||||
"fps": 15,
|
||||
"codec": "video/H264"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 465,
|
||||
"outgoingVideoFramesAccepted": 465,
|
||||
"outgoingVideoFramesDropped": 9,
|
||||
"outgoingVideoFramesCoalesced": 9,
|
||||
"outgoingVideoFramesCaptured": 456,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 14.7,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 67,
|
||||
"outgoingVideoMaxPushLatencyMs": 68,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 45,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_jmECWh8cUV8Z",
|
||||
"trackSid": "TR_VSu5oXdquxv9yn",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1957.3,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 4,
|
||||
"codec": "video/H264"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_jmECWh8cUV8Z",
|
||||
"trackSid": "TR_AMw39xhBtq2iKo",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 97.9,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.33,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_jmECWh8cUV8Z",
|
||||
"trackSid": "TR_AsUfFKczDubKFH",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 263,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.342,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-258
@@ -1,258 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T22:35:16.027Z",
|
||||
"endedAt": "2026-06-05T22:35:17.121Z",
|
||||
"platform": "win32",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-6712-42fbb624-d6ae-4495-80c6-f657bba60b68",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-6712",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-6712",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-6712",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 60000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 10000,
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"expectedWidth": 640,
|
||||
"expectedHeight": 360,
|
||||
"fps": 15,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 150,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": false,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["h265"],
|
||||
"expectedScreenCodecs": ["h265"],
|
||||
"codec": "h265",
|
||||
"expectedScreenCodec": "video/H265",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "h265",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/H265",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 4000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": false,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\h265-failfast-20260605.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-h265",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 640x360",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 6",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 2",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 6",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/H265",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/H265",
|
||||
"pass": false
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "Error: H.265 publishing requires hardware encoder support; missing nvcuda.dll, nvEncodeAPI64.dll",
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "runtime_prerequisite_missing",
|
||||
"detail": "missing nvcuda.dll, nvEncodeAPI64.dll"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 0,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 0,
|
||||
"videoCallbacks": 0,
|
||||
"videoBytes": 0,
|
||||
"receivedFps": 0,
|
||||
"requiredFps": 14.25,
|
||||
"maxVideoFrameGapMs": 0,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 0,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": null,
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": null,
|
||||
"error": null,
|
||||
"participants": []
|
||||
},
|
||||
"lastVideoMeta": null,
|
||||
"publisherStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
param(
|
||||
[string]$Scenario = "vp8-360p15-strict-single",
|
||||
[string]$Codec = "vp8",
|
||||
[string]$SecondaryCodec = "",
|
||||
[int]$Width = 640,
|
||||
[int]$Height = 360,
|
||||
[double]$Fps = 15,
|
||||
[int]$DurationMs = 10000,
|
||||
[int]$TimeoutMs = 60000,
|
||||
[int]$SecondPublisher = 0,
|
||||
[int]$ScreenSimulcast = 0,
|
||||
[int]$ScreenAudio = 1,
|
||||
[int]$Microphone = 1,
|
||||
[int]$SubscriptionCycle = 1,
|
||||
[int]$DataPacket = 1,
|
||||
[int]$ValidateServerPublishing = 1,
|
||||
[int]$RequireStableResolution = 1,
|
||||
[int]$MaxPacketLoss = 0,
|
||||
[int]$MaxAvDriftMs = 150,
|
||||
[int]$MaxFrameGapMs = 250,
|
||||
[int]$MaxAudioFrameGapMs = 250,
|
||||
[double]$MinReceivedFpsRatio = 0.95,
|
||||
[int]$MaxBitrateBps = 4000000,
|
||||
[string]$LiveKitUrl = "ws://100.79.83.54:17880",
|
||||
[string]$LiveKitApiUrl = "http://100.79.83.54:17880",
|
||||
[string]$ApiKey = "devkey",
|
||||
[string]$ApiSecret = "secret",
|
||||
[string]$NodeDir = "C:\nodejs\node-v24.16.0-win-arm64",
|
||||
[string]$WorkDir = "C:\f\fluxer_desktop\native\webrtc-sender",
|
||||
[string]$ReportDir = "C:\Mac\Home\Development\fluxer\fluxer_desktop\native-media-reports\windows-parallels-arm64",
|
||||
[int]$VerboseHarness = 0
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$nodeExe = Join-Path $NodeDir "node.exe"
|
||||
if (-not (Test-Path $nodeExe)) {
|
||||
throw "Node executable not found: $nodeExe"
|
||||
}
|
||||
if (-not (Test-Path $WorkDir)) {
|
||||
throw "WorkDir not found: $WorkDir"
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $ReportDir | Out-Null
|
||||
$reportPath = Join-Path $ReportDir "$Scenario.json"
|
||||
$logPath = Join-Path $ReportDir "$Scenario.log"
|
||||
Remove-Item -Force $reportPath, $logPath -ErrorAction SilentlyContinue
|
||||
|
||||
$env:Path = "$NodeDir;$env:Path"
|
||||
$env:LIVEKIT_URL = $LiveKitUrl
|
||||
$env:LIVEKIT_API_URL = $LiveKitApiUrl
|
||||
$env:LIVEKIT_API_KEY = $ApiKey
|
||||
$env:LIVEKIT_API_SECRET = $ApiSecret
|
||||
$env:FLUXER_WEBRTC_SENDER_LIVEKIT_REQUIRED = "1"
|
||||
$env:LIVEKIT_HARNESS_STRICT = "1"
|
||||
$env:LIVEKIT_HARNESS_DURATION_MS = [string]$DurationMs
|
||||
$env:LIVEKIT_HARNESS_TIMEOUT_MS = [string]$TimeoutMs
|
||||
$env:LIVEKIT_CONNECT_TIMEOUT_MS = "30000"
|
||||
$env:LIVEKIT_SCREEN_WIDTH = [string]$Width
|
||||
$env:LIVEKIT_SCREEN_HEIGHT = [string]$Height
|
||||
$env:LIVEKIT_SCREEN_FPS = [string]$Fps
|
||||
$env:LIVEKIT_MIN_VIDEO_FPS = [string]([math]::Min(15, $Fps))
|
||||
$env:LIVEKIT_MIN_RECEIVED_FPS_RATIO = [string]$MinReceivedFpsRatio
|
||||
$env:LIVEKIT_SCREEN_CODEC = $Codec
|
||||
$env:LIVEKIT_SCREEN_SIMULCAST = [string]$ScreenSimulcast
|
||||
$env:LIVEKIT_ENABLE_SECOND_PUBLISHER = [string]$SecondPublisher
|
||||
$env:LIVEKIT_ENABLE_MICROPHONE = [string]$Microphone
|
||||
$env:LIVEKIT_ENABLE_SCREEN_AUDIO = [string]$ScreenAudio
|
||||
$env:LIVEKIT_ENABLE_SUBSCRIPTION_CYCLE = [string]$SubscriptionCycle
|
||||
$env:LIVEKIT_ENABLE_DATA_PACKET = [string]$DataPacket
|
||||
$env:LIVEKIT_VALIDATE_SERVER_PUBLISHING = [string]$ValidateServerPublishing
|
||||
$env:LIVEKIT_MAX_PACKET_LOSS = [string]$MaxPacketLoss
|
||||
$env:LIVEKIT_REQUIRE_STABLE_RESOLUTION = [string]$RequireStableResolution
|
||||
$env:LIVEKIT_MAX_AV_DRIFT_MS = [string]$MaxAvDriftMs
|
||||
$env:LIVEKIT_MAX_FRAME_GAP_MS = [string]$MaxFrameGapMs
|
||||
$env:LIVEKIT_MAX_AUDIO_FRAME_GAP_MS = [string]$MaxAudioFrameGapMs
|
||||
$env:LIVEKIT_SCREEN_MAX_BITRATE_BPS = [string]$MaxBitrateBps
|
||||
$env:LIVEKIT_HARNESS_REPORT_PATH = $reportPath
|
||||
$env:LIVEKIT_VERBOSE = [string]$VerboseHarness
|
||||
|
||||
if ($SecondPublisher -ne 0 -and $SecondaryCodec.Trim().Length -gt 0) {
|
||||
$env:LIVEKIT_SECOND_PUBLISHER_SCREEN_CODEC = $SecondaryCodec
|
||||
}
|
||||
|
||||
Set-Location $WorkDir
|
||||
"STARTED=$(Get-Date -Format o)" | Out-File -FilePath $logPath -Encoding utf8
|
||||
"& $nodeExe scripts/livekit-harness.mjs" | Add-Content -Path $logPath
|
||||
$previousErrorActionPreference = $ErrorActionPreference
|
||||
$ErrorActionPreference = "Continue"
|
||||
& $nodeExe "scripts/livekit-harness.mjs" *>> $logPath
|
||||
$code = $LASTEXITCODE
|
||||
$ErrorActionPreference = $previousErrorActionPreference
|
||||
"EXIT_CODE=$code" | Add-Content -Path $logPath
|
||||
"FINISHED=$(Get-Date -Format o)" | Add-Content -Path $logPath
|
||||
|
||||
Get-Content -Path $logPath
|
||||
exit $code
|
||||
-637
@@ -1,637 +0,0 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"startedAt": "2026-06-05T22:34:34.390Z",
|
||||
"endedAt": "2026-06-05T22:34:56.901Z",
|
||||
"platform": "win32",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-576-9baa1b50-b3f6-47dc-9222-bb3a324a8678",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-576",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-576",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-576",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 120000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 20000,
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"expectedWidth": 640,
|
||||
"expectedHeight": 360,
|
||||
"fps": 15,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 150,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": true,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 4000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\vp8-360p15-strict-multipub-20260605.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8-with-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 640x360",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 4",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 12",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound secondary codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote secondary screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "secondary publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber secondary data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees secondary publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": null,
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "runtime_prerequisite_missing",
|
||||
"detail": "missing nvcuda.dll, nvEncodeAPI64.dll"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 8386,
|
||||
"inboundVideoFrames": 605,
|
||||
"strictAudioFrames": 199,
|
||||
"strictVideoFrames": 588,
|
||||
"videoCallbacks": 605,
|
||||
"videoBytes": 209088000,
|
||||
"receivedFps": 29.42,
|
||||
"requiredFps": 14.25,
|
||||
"maxVideoFrameGapMs": 129,
|
||||
"maxAudioFrameGapMs": 108,
|
||||
"maxVideoFrameGapOverallMs": 129,
|
||||
"maxAudioFrameGapOverallMs": 112,
|
||||
"avDriftMs": 5,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"640x360": 588
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 15,
|
||||
"startedAtMs": 1780698875820,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 317,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 74,
|
||||
"lastPushAtMs": 1780698896886,
|
||||
"elapsedMs": 21084,
|
||||
"producedFps": 15.04
|
||||
},
|
||||
"secondaryPublisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 15,
|
||||
"startedAtMs": 1780698875820,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 317,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 74,
|
||||
"lastPushAtMs": 1780698896888,
|
||||
"elapsedMs": 21084,
|
||||
"producedFps": 15.04
|
||||
},
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780698876883,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-2-576",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_AMafY4CpWgYhFy",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsEzCDkQT4wKAK",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_VSbd2RjXnY3stM",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-576",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-576",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSc4kF32666sNr",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMrQCsApajeKHi",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsX4E2jVX6P6XY",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_adnUMDZZHzat",
|
||||
"trackSid": "TR_VSc4kF32666sNr",
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"timestampUs": 1774721000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AsX4E2jVX6P6XY",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 258.8,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSc4kF32666sNr",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1259.5,
|
||||
"packetsLost": 0,
|
||||
"fps": 15,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMrQCsApajeKHi",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.2,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_A8vwT8LTQovP",
|
||||
"trackSid": "TR_AMafY4CpWgYhFy",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99.8,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"audioLevel": 0.306,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_A8vwT8LTQovP",
|
||||
"trackSid": "TR_VSbd2RjXnY3stM",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1257.1,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 5,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_A8vwT8LTQovP",
|
||||
"trackSid": "TR_AsEzCDkQT4wKAK",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 264.5,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"audioLevel": 0.306,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 310,
|
||||
"outgoingVideoFramesAccepted": 310,
|
||||
"outgoingVideoFramesDropped": 6,
|
||||
"outgoingVideoFramesCoalesced": 6,
|
||||
"outgoingVideoFramesCaptured": 304,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 14.7,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 67,
|
||||
"outgoingVideoMaxPushLatencyMs": 67,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 51,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": {
|
||||
"rttMs": 2,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AsEzCDkQT4wKAK",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 259.9,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSbd2RjXnY3stM",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1171.6,
|
||||
"packetsLost": 0,
|
||||
"fps": 15,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMafY4CpWgYhFy",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.1,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_adnUMDZZHzat",
|
||||
"trackSid": "TR_AsX4E2jVX6P6XY",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 259.2,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_adnUMDZZHzat",
|
||||
"trackSid": "TR_AMrQCsApajeKHi",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"audioLevel": 0.326,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_adnUMDZZHzat",
|
||||
"trackSid": "TR_VSc4kF32666sNr",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1176.7,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 4,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 312,
|
||||
"outgoingVideoFramesAccepted": 312,
|
||||
"outgoingVideoFramesDropped": 6,
|
||||
"outgoingVideoFramesCoalesced": 6,
|
||||
"outgoingVideoFramesCaptured": 306,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 14.7,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 67,
|
||||
"outgoingVideoMaxPushLatencyMs": 68,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 51,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"subscriberStats": {
|
||||
"rttMs": 2,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_adnUMDZZHzat",
|
||||
"trackSid": "TR_AsX4E2jVX6P6XY",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 258.8,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"audioLevel": 0.336,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_adnUMDZZHzat",
|
||||
"trackSid": "TR_VSc4kF32666sNr",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1259.5,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 4,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_adnUMDZZHzat",
|
||||
"trackSid": "TR_AMrQCsApajeKHi",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.2,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_A8vwT8LTQovP",
|
||||
"trackSid": "TR_AMafY4CpWgYhFy",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99.7,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"audioLevel": 0.306,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_A8vwT8LTQovP",
|
||||
"trackSid": "TR_AsEzCDkQT4wKAK",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 264.3,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_A8vwT8LTQovP",
|
||||
"trackSid": "TR_VSbd2RjXnY3stM",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1256,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 6,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-391
@@ -1,391 +0,0 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"startedAt": "2026-06-05T22:30:31.342Z",
|
||||
"endedAt": "2026-06-05T22:30:43.694Z",
|
||||
"platform": "win32",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-3164-5cfbcff1-2827-441a-a57b-4333e450d829",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-3164",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-3164",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-3164",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 60000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 10000,
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"expectedWidth": 640,
|
||||
"expectedHeight": 360,
|
||||
"fps": 15,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 150,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 4000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\vp8-360p15-strict-single-smoke-20260605.json",
|
||||
"verbose": true,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 640x360",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": null,
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "runtime_prerequisite_missing",
|
||||
"detail": "missing nvcuda.dll, nvEncodeAPI64.dll"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 2156,
|
||||
"inboundVideoFrames": 152,
|
||||
"strictAudioFrames": 100,
|
||||
"strictVideoFrames": 147,
|
||||
"videoCallbacks": 152,
|
||||
"videoBytes": 52531200,
|
||||
"receivedFps": 14.66,
|
||||
"requiredFps": 14.25,
|
||||
"maxVideoFrameGapMs": 143,
|
||||
"maxAudioFrameGapMs": 105,
|
||||
"maxVideoFrameGapOverallMs": 143,
|
||||
"maxAudioFrameGapOverallMs": 325,
|
||||
"avDriftMs": 26,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"640x360": 147
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 15,
|
||||
"startedAtMs": 1780698632580,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 163,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 355,
|
||||
"lastPushAtMs": 1780698643669,
|
||||
"elapsedMs": 11117,
|
||||
"producedFps": 14.66
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780698633676,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-3164",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-3164",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSD8qaPQikwWiL",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMMkiV8hg3XbkW",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsTaYjh3PSCFVz",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_fME8NrPiTu5d",
|
||||
"trackSid": "TR_VSD8qaPQikwWiL",
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"timestampUs": 1521475000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 3,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AMMkiV8hg3XbkW",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.9,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSD8qaPQikwWiL",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1233,
|
||||
"packetsLost": 0,
|
||||
"fps": 15,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AsTaYjh3PSCFVz",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 256.8,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 161,
|
||||
"outgoingVideoFramesAccepted": 161,
|
||||
"outgoingVideoFramesDropped": 2,
|
||||
"outgoingVideoFramesCoalesced": 2,
|
||||
"outgoingVideoFramesCaptured": 158,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 14.36,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 66,
|
||||
"outgoingVideoMaxPushLatencyMs": 66,
|
||||
"outgoingAudioBufferTargetMs": 300,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 1,
|
||||
"outgoingAudioRebuffers": 1,
|
||||
"outgoingAudioMaxFrameGapMs": 316,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_fME8NrPiTu5d",
|
||||
"trackSid": "TR_VSD8qaPQikwWiL",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1158.7,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 22,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_fME8NrPiTu5d",
|
||||
"trackSid": "TR_AsTaYjh3PSCFVz",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 252.2,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.336,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_fME8NrPiTu5d",
|
||||
"trackSid": "TR_AMMkiV8hg3XbkW",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.332,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-391
@@ -1,391 +0,0 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"startedAt": "2026-06-05T22:39:23.612Z",
|
||||
"endedAt": "2026-06-05T22:39:35.835Z",
|
||||
"platform": "win32",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-8360-3df01e6b-06f6-4b3c-9e56-c691627bb305",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-8360",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-8360",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-8360",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 60000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 10000,
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"expectedWidth": 640,
|
||||
"expectedHeight": 360,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 150,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 5000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\vp8-360p30-reason-after-fix-20260605.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 640x360",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": null,
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "runtime_prerequisite_missing",
|
||||
"detail": "missing nvcuda.dll, nvEncodeAPI64.dll"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 2170,
|
||||
"inboundVideoFrames": 299,
|
||||
"strictAudioFrames": 99,
|
||||
"strictVideoFrames": 291,
|
||||
"videoCallbacks": 299,
|
||||
"videoBytes": 103334400,
|
||||
"receivedFps": 29.05,
|
||||
"requiredFps": 28.5,
|
||||
"maxVideoFrameGapMs": 65,
|
||||
"maxAudioFrameGapMs": 109,
|
||||
"maxVideoFrameGapOverallMs": 195,
|
||||
"maxAudioFrameGapOverallMs": 109,
|
||||
"avDriftMs": 41,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"640x360": 291
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780699164743,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 333,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 52,
|
||||
"lastPushAtMs": 1780699175812,
|
||||
"elapsedMs": 11095,
|
||||
"producedFps": 30.01
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780699165815,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-8360",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-8360",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_AMSaSBEwb9cjwL",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_Ash3ybc8XXbukk",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_VSzotHy6zMQsRG",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_iYVBCmnrZuSb",
|
||||
"trackSid": "TR_VSzotHy6zMQsRG",
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"timestampUs": 2053643000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AMSaSBEwb9cjwL",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_Ash3ybc8XXbukk",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 265.1,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSzotHy6zMQsRG",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 2414.2,
|
||||
"packetsLost": 0,
|
||||
"fps": 29,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 329,
|
||||
"outgoingVideoFramesAccepted": 329,
|
||||
"outgoingVideoFramesDropped": 13,
|
||||
"outgoingVideoFramesCoalesced": 13,
|
||||
"outgoingVideoFramesCaptured": 316,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 28.84,
|
||||
"outgoingVideoTargetFps": 30,
|
||||
"outgoingVideoMaxQueueAgeMs": 34,
|
||||
"outgoingVideoMaxPushLatencyMs": 35,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 42,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_iYVBCmnrZuSb",
|
||||
"trackSid": "TR_Ash3ybc8XXbukk",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 265.1,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.318,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_iYVBCmnrZuSb",
|
||||
"trackSid": "TR_AMSaSBEwb9cjwL",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.324,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_iYVBCmnrZuSb",
|
||||
"trackSid": "TR_VSzotHy6zMQsRG",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 2414.2,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 4,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-391
@@ -1,391 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T22:35:40.815Z",
|
||||
"endedAt": "2026-06-05T22:36:13.026Z",
|
||||
"platform": "win32",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-6332-943a2620-7cc8-4881-b1a9-6d8577e9c434",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-6332",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-6332",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-6332",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 120000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 30000,
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"expectedWidth": 640,
|
||||
"expectedHeight": 360,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 150,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 5000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\vp8-360p30-strict-single-20260605.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 640x360",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "received FPS 15.03 below 28.5",
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "runtime_prerequisite_missing",
|
||||
"detail": "missing nvcuda.dll, nvEncodeAPI64.dll"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 6168,
|
||||
"inboundVideoFrames": 461,
|
||||
"strictAudioFrames": 299,
|
||||
"strictVideoFrames": 451,
|
||||
"videoCallbacks": 461,
|
||||
"videoBytes": 159321600,
|
||||
"receivedFps": 15.03,
|
||||
"requiredFps": 28.5,
|
||||
"maxVideoFrameGapMs": 137,
|
||||
"maxAudioFrameGapMs": 110,
|
||||
"maxVideoFrameGapOverallMs": 137,
|
||||
"maxAudioFrameGapOverallMs": 110,
|
||||
"avDriftMs": 22,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"640x360": 451
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780698941926,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 933,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 63,
|
||||
"lastPushAtMs": 1780698972994,
|
||||
"elapsedMs": 31105,
|
||||
"producedFps": 30
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780698943004,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-6332",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-6332",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSBtiuMi7Xz4iL",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsSNqz63GYafY3",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMjupdo38YrBp7",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_FcwLkjDchP7J",
|
||||
"trackSid": "TR_VSBtiuMi7Xz4iL",
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"timestampUs": 1850800000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 2,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AsSNqz63GYafY3",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 261.4,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMjupdo38YrBp7",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99.5,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSBtiuMi7Xz4iL",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1258.5,
|
||||
"packetsLost": 0,
|
||||
"fps": 15,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 930,
|
||||
"outgoingVideoFramesAccepted": 930,
|
||||
"outgoingVideoFramesDropped": 402,
|
||||
"outgoingVideoFramesCoalesced": 402,
|
||||
"outgoingVideoFramesCaptured": 527,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 17,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 62,
|
||||
"outgoingVideoMaxPushLatencyMs": 62,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 60,
|
||||
"adaptiveSendTier": "fps15",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 2,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_FcwLkjDchP7J",
|
||||
"trackSid": "TR_AsSNqz63GYafY3",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 261.4,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"audioLevel": 0.319,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_FcwLkjDchP7J",
|
||||
"trackSid": "TR_AMjupdo38YrBp7",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99.5,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.32,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_FcwLkjDchP7J",
|
||||
"trackSid": "TR_VSBtiuMi7Xz4iL",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1258.5,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-391
@@ -1,391 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T22:39:58.041Z",
|
||||
"endedAt": "2026-06-05T22:40:30.271Z",
|
||||
"platform": "win32",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-3512-db87cf14-fb04-4daf-84ee-a577ef39b456",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-3512",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-3512",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-3512",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 120000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 30000,
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"expectedWidth": 640,
|
||||
"expectedHeight": 360,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 150,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 5000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\vp8-360p30-strict-single-after-fix-20260605.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 640x360",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "received FPS 16.35 below 28.5",
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "runtime_prerequisite_missing",
|
||||
"detail": "missing nvcuda.dll, nvEncodeAPI64.dll"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 6178,
|
||||
"inboundVideoFrames": 503,
|
||||
"strictAudioFrames": 297,
|
||||
"strictVideoFrames": 490,
|
||||
"videoCallbacks": 503,
|
||||
"videoBytes": 173836800,
|
||||
"receivedFps": 16.35,
|
||||
"requiredFps": 28.5,
|
||||
"maxVideoFrameGapMs": 244,
|
||||
"maxAudioFrameGapMs": 180,
|
||||
"maxVideoFrameGapOverallMs": 244,
|
||||
"maxAudioFrameGapOverallMs": 180,
|
||||
"avDriftMs": 60,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"640x360": 490
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780699199141,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 931,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 155,
|
||||
"lastPushAtMs": 1780699230263,
|
||||
"elapsedMs": 31136,
|
||||
"producedFps": 29.9
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780699200252,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-3512",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSFLKMrtaZRUAL",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMTrMJygXoaHT8",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AseREdJhgwsKHc",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-3512",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_BbRoKiddPYPe",
|
||||
"trackSid": "TR_VSFLKMrtaZRUAL",
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"timestampUs": 2108079000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_VSFLKMrtaZRUAL",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1257.6,
|
||||
"packetsLost": 0,
|
||||
"fps": 15,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AseREdJhgwsKHc",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 257.1,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMTrMJygXoaHT8",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99.1,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 927,
|
||||
"outgoingVideoFramesAccepted": 927,
|
||||
"outgoingVideoFramesDropped": 405,
|
||||
"outgoingVideoFramesCoalesced": 405,
|
||||
"outgoingVideoFramesCaptured": 521,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 16.8,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 74,
|
||||
"outgoingVideoMaxPushLatencyMs": 76,
|
||||
"outgoingAudioBufferTargetMs": 300,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 1,
|
||||
"outgoingAudioMaxFrameGapMs": 153,
|
||||
"adaptiveSendTier": "fps15",
|
||||
"adaptiveSendReason": "sendLatencyPressure"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_BbRoKiddPYPe",
|
||||
"trackSid": "TR_AseREdJhgwsKHc",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 252.8,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.337,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_BbRoKiddPYPe",
|
||||
"trackSid": "TR_VSFLKMrtaZRUAL",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1169,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 3,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_BbRoKiddPYPe",
|
||||
"trackSid": "TR_AMTrMJygXoaHT8",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 97.5,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.315,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-391
@@ -1,391 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T22:31:55.148Z",
|
||||
"endedAt": "2026-06-05T22:32:27.616Z",
|
||||
"platform": "win32",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-1340-0367e318-dcfc-498a-8724-0c3982583d5d",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-1340",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-1340",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-1340",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 120000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 30000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 15,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 150,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 8000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\vp8-720p15-strict-single-20260605.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "received FPS 11.06 below 14.25; max video frame gap 363ms exceeds 250ms",
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "runtime_prerequisite_missing",
|
||||
"detail": "missing nvcuda.dll, nvEncodeAPI64.dll"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 6230,
|
||||
"inboundVideoFrames": 334,
|
||||
"strictAudioFrames": 298,
|
||||
"strictVideoFrames": 331,
|
||||
"videoCallbacks": 334,
|
||||
"videoBytes": 461721600,
|
||||
"receivedFps": 11.06,
|
||||
"requiredFps": 14.25,
|
||||
"maxVideoFrameGapMs": 363,
|
||||
"maxAudioFrameGapMs": 108,
|
||||
"maxVideoFrameGapOverallMs": 363,
|
||||
"maxAudioFrameGapOverallMs": 108,
|
||||
"avDriftMs": 38,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 331
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 15,
|
||||
"startedAtMs": 1780698716263,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 471,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 93,
|
||||
"lastPushAtMs": 1780698747600,
|
||||
"elapsedMs": 31355,
|
||||
"producedFps": 15.02
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780698717342,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-1340",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-1340",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VSYp54vvSodQtk",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMtAPT5gLdZFiG",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsyzCPFpjfCZ52",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_f3pUVwWqpSf9",
|
||||
"trackSid": "TR_VSYp54vvSodQtk",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 1625403000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 3,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AsyzCPFpjfCZ52",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 324.8,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSYp54vvSodQtk",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 5042,
|
||||
"packetsLost": 0,
|
||||
"fps": 15,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMtAPT5gLdZFiG",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99.4,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 465,
|
||||
"outgoingVideoFramesAccepted": 465,
|
||||
"outgoingVideoFramesDropped": 9,
|
||||
"outgoingVideoFramesCoalesced": 9,
|
||||
"outgoingVideoFramesCaptured": 456,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 14.71,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 66,
|
||||
"outgoingVideoMaxPushLatencyMs": 68,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 47,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 3,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_f3pUVwWqpSf9",
|
||||
"trackSid": "TR_AMtAPT5gLdZFiG",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 99.4,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"audioLevel": 0.32,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_f3pUVwWqpSf9",
|
||||
"trackSid": "TR_VSYp54vvSodQtk",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 5042,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 33,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_f3pUVwWqpSf9",
|
||||
"trackSid": "TR_AsyzCPFpjfCZ52",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 324.8,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.344,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-391
@@ -1,391 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T22:30:59.267Z",
|
||||
"endedAt": "2026-06-05T22:31:31.489Z",
|
||||
"platform": "win32",
|
||||
"arch": "arm64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-5096-0b21498f-6c31-4ed8-8e45-930895fdc2d2",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-5096",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-5096",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-5096",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 120000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 30000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 150,
|
||||
"maxPacketLoss": 0,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 8000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\vp8-720p30-strict-single-20260605.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "received FPS 15.97 below 28.5",
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "runtime_prerequisite_missing",
|
||||
"detail": "missing nvcuda.dll, nvEncodeAPI64.dll"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 6178,
|
||||
"inboundVideoFrames": 482,
|
||||
"strictAudioFrames": 298,
|
||||
"strictVideoFrames": 479,
|
||||
"videoCallbacks": 482,
|
||||
"videoBytes": 666316800,
|
||||
"receivedFps": 15.97,
|
||||
"requiredFps": 28.5,
|
||||
"maxVideoFrameGapMs": 102,
|
||||
"maxAudioFrameGapMs": 130,
|
||||
"maxVideoFrameGapOverallMs": 168,
|
||||
"maxAudioFrameGapOverallMs": 130,
|
||||
"avDriftMs": 43,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 479
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780698660405,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 933,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 68,
|
||||
"lastPushAtMs": 1780698691475,
|
||||
"elapsedMs": 31087,
|
||||
"producedFps": 30.01
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780698661479,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-5096",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-publisher-5096",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_VS2iFXiujdx95V",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AMNf9uVyTmBcZy",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsZpfieifQxEYA",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_XJp9ZzhWYzHd",
|
||||
"trackSid": "TR_VS2iFXiujdx95V",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 1569321000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 1,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AMNf9uVyTmBcZy",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.4,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AsZpfieifQxEYA",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 269.5,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VS2iFXiujdx95V",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 5155.1,
|
||||
"packetsLost": 0,
|
||||
"fps": 15,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 930,
|
||||
"outgoingVideoFramesAccepted": 930,
|
||||
"outgoingVideoFramesDropped": 403,
|
||||
"outgoingVideoFramesCoalesced": 403,
|
||||
"outgoingVideoFramesCaptured": 526,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 16.97,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 45,
|
||||
"outgoingVideoMaxPushLatencyMs": 46,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 57,
|
||||
"adaptiveSendTier": "fps15",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 3,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_XJp9ZzhWYzHd",
|
||||
"trackSid": "TR_VS2iFXiujdx95V",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 5155.1,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 11,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_XJp9ZzhWYzHd",
|
||||
"trackSid": "TR_AsZpfieifQxEYA",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 269.5,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_XJp9ZzhWYzHd",
|
||||
"trackSid": "TR_AMNf9uVyTmBcZy",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 98.4,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 1,
|
||||
"audioLevel": 0.336,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-391
@@ -1,391 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T21:57:48.945Z",
|
||||
"endedAt": "2026-06-05T21:58:12.868Z",
|
||||
"platform": "win32",
|
||||
"arch": "x64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-1272-581b0bd8-4044-45a9-bb0d-695ca988d59c",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-1272",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-1272",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-1272",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 30000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 20000,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"expectedWidth": 1280,
|
||||
"expectedHeight": 720,
|
||||
"fps": 30,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 80,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["h264"],
|
||||
"expectedScreenCodecs": ["h264"],
|
||||
"codec": "h264",
|
||||
"expectedScreenCodec": "video/H264",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "h264",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/H264",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 5000000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "C:\\f\\fluxer_desktop\\native\\webrtc-sender\\native-media-reports\\windows-mx450-livekit-h264-single-egress-adaptive-20260605.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-h264",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 1280x720",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/H264",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "received FPS 6.59 below 28.5; max video frame gap 552ms exceeds 250ms",
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "no_supported_devices",
|
||||
"detail": "missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 4315,
|
||||
"inboundVideoFrames": 136,
|
||||
"strictAudioFrames": 199,
|
||||
"strictVideoFrames": 132,
|
||||
"videoCallbacks": 135,
|
||||
"videoBytes": 186624000,
|
||||
"receivedFps": 6.59,
|
||||
"requiredFps": 28.5,
|
||||
"maxVideoFrameGapMs": 552,
|
||||
"maxAudioFrameGapMs": 112,
|
||||
"maxVideoFrameGapOverallMs": 557,
|
||||
"maxAudioFrameGapOverallMs": 112,
|
||||
"avDriftMs": 22,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"1280x720": 132
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 30,
|
||||
"startedAtMs": 1780696671200,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 650,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 64,
|
||||
"lastPushAtMs": 1780696692841,
|
||||
"elapsedMs": 21772,
|
||||
"producedFps": 29.85
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780696672853,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-1272",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_AM3TQzwbFTcoXT",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_AsW4JLnmWG6cce",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_VSxrpLn4EJfUEg",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/H264",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-1272",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_7EoucgabpBG8",
|
||||
"trackSid": "TR_VSxrpLn4EJfUEg",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"timestampUs": 3306206000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 15,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_AM3TQzwbFTcoXT",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 95.3,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VSxrpLn4EJfUEg",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 5106.2,
|
||||
"packetsLost": 0,
|
||||
"fps": 11,
|
||||
"codec": "video/H264"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AsW4JLnmWG6cce",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 300.6,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 627,
|
||||
"outgoingVideoFramesAccepted": 627,
|
||||
"outgoingVideoFramesDropped": 262,
|
||||
"outgoingVideoFramesCoalesced": 262,
|
||||
"outgoingVideoFramesCaptured": 364,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 17.42,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 35,
|
||||
"outgoingVideoMaxPushLatencyMs": 42,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 63,
|
||||
"adaptiveSendTier": "fps15",
|
||||
"adaptiveSendReason": "encoderEgressPressure"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 18,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_7EoucgabpBG8",
|
||||
"trackSid": "TR_AsW4JLnmWG6cce",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 296.3,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 6,
|
||||
"audioLevel": 0.336,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_7EoucgabpBG8",
|
||||
"trackSid": "TR_AM3TQzwbFTcoXT",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 97.1,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 7,
|
||||
"audioLevel": 0.326,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_7EoucgabpBG8",
|
||||
"trackSid": "TR_VSxrpLn4EJfUEg",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 5397.8,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 48,
|
||||
"codec": "video/H264"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-246
@@ -1,246 +0,0 @@
|
||||
{
|
||||
"status": "fail",
|
||||
"startedAt": "2026-06-05T22:00:49.164Z",
|
||||
"endedAt": "2026-06-05T22:00:50.336Z",
|
||||
"platform": "win32",
|
||||
"arch": "x64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-3456-3aa0da22-e543-4846-a980-1ef479694d1f",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-3456",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-3456",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-3456",
|
||||
"required": true,
|
||||
"strict": false,
|
||||
"timeoutMs": 30000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 0,
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"expectedWidth": 640,
|
||||
"expectedHeight": 360,
|
||||
"fps": 15,
|
||||
"minFpsRatio": 0,
|
||||
"maxFrameGapMs": 0,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxAvDriftMs": 0,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": false,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": null,
|
||||
"screenCodecs": ["hevc"],
|
||||
"expectedScreenCodecs": ["hevc"],
|
||||
"codec": "hevc",
|
||||
"expectedScreenCodec": "video/H265",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "hevc",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/H265",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": true,
|
||||
"secondaryPublisherScreenSimulcast": true,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": false,
|
||||
"maxBitrateBps": 400000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": false,
|
||||
"dataPacket": false,
|
||||
"subscriptionCycle": false,
|
||||
"validateServerPublishing": false,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "C:\\f\\fluxer_desktop\\native\\webrtc-sender\\native-media-reports\\windows-mx450-livekit-hevc-failfast-20260605.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-hevc",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 640x360",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 3",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 1",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 3",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/H265",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/H265",
|
||||
"pass": false
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": "Error: H.265 publishing requires hardware encoder support; missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))",
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "no_supported_devices",
|
||||
"detail": "missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 0,
|
||||
"inboundVideoFrames": 0,
|
||||
"strictAudioFrames": 0,
|
||||
"strictVideoFrames": 0,
|
||||
"videoCallbacks": 0,
|
||||
"videoBytes": 0,
|
||||
"receivedFps": 0,
|
||||
"requiredFps": null,
|
||||
"maxVideoFrameGapMs": 0,
|
||||
"maxAudioFrameGapMs": 0,
|
||||
"maxVideoFrameGapOverallMs": 0,
|
||||
"maxAudioFrameGapOverallMs": 0,
|
||||
"avDriftMs": null,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": null,
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": null,
|
||||
"error": null,
|
||||
"participants": []
|
||||
},
|
||||
"lastVideoMeta": null,
|
||||
"publisherStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": null,
|
||||
"outbound": [],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-391
@@ -1,391 +0,0 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"startedAt": "2026-06-05T22:00:19.291Z",
|
||||
"endedAt": "2026-06-05T22:00:37.423Z",
|
||||
"platform": "win32",
|
||||
"arch": "x64",
|
||||
"config": {
|
||||
"url": "ws://100.79.83.54:17880",
|
||||
"serverApiUrl": "http://100.79.83.54:17880",
|
||||
"apiKey": "devkey",
|
||||
"room": "fluxer-webrtc-harness-6872-db238db9-c867-4b03-8af9-9740942a674c",
|
||||
"externalTokens": false,
|
||||
"publisherIdentity": "fluxer-native-publisher-6872",
|
||||
"secondaryPublisherIdentity": "fluxer-native-publisher-2-6872",
|
||||
"subscriberIdentity": "fluxer-native-subscriber-6872",
|
||||
"required": true,
|
||||
"strict": true,
|
||||
"timeoutMs": 30000,
|
||||
"connectTimeoutMs": 30000,
|
||||
"disconnectTimeoutMs": 10000,
|
||||
"probeTimeoutMs": 1500,
|
||||
"durationMs": 15000,
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"expectedWidth": 640,
|
||||
"expectedHeight": 360,
|
||||
"fps": 15,
|
||||
"minFpsRatio": 0.95,
|
||||
"maxFrameGapMs": 250,
|
||||
"maxAudioFrameGapMs": 250,
|
||||
"maxAvDriftMs": 150,
|
||||
"maxPacketLoss": null,
|
||||
"requireStableResolution": true,
|
||||
"videoPattern": "gradient",
|
||||
"videoInput": "bgra",
|
||||
"subscriberVideoQuality": "high",
|
||||
"screenCodecs": ["vp8"],
|
||||
"expectedScreenCodecs": ["vp8"],
|
||||
"codec": "vp8",
|
||||
"expectedScreenCodec": "video/VP8",
|
||||
"secondaryPublisher": false,
|
||||
"secondaryPublisherCodec": "vp8",
|
||||
"secondaryPublisherCodecExplicit": false,
|
||||
"expectedSecondaryPublisherCodec": "video/VP8",
|
||||
"expectedSecondaryPublisherCodecExplicit": false,
|
||||
"screenSimulcast": false,
|
||||
"secondaryPublisherScreenSimulcast": false,
|
||||
"adaptiveSend": true,
|
||||
"minVideoFps": 15,
|
||||
"minResolutionScale": 0.5,
|
||||
"maxAudioBufferMs": 750,
|
||||
"secondaryPublisherMicrophone": true,
|
||||
"secondaryPublisherScreenAudio": true,
|
||||
"maxBitrateBps": 1200000,
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"audioChunkMs": 20,
|
||||
"microphone": true,
|
||||
"screenAudio": true,
|
||||
"dataPacket": true,
|
||||
"subscriptionCycle": true,
|
||||
"validateServerPublishing": true,
|
||||
"camera": false,
|
||||
"cameraDeviceId": null,
|
||||
"expectedCameraCodec": null,
|
||||
"expectedHardwareEncoder": null,
|
||||
"reportPath": "C:\\f\\fluxer_desktop\\native\\webrtc-sender\\native-media-reports\\windows-mx450-livekit-vp8-360p15-strict-av150-20260605.json",
|
||||
"verbose": false,
|
||||
"scenarioName": "codec-vp8",
|
||||
"apiSecret": "<redacted>",
|
||||
"e2eeKey": null,
|
||||
"publisherToken": null,
|
||||
"secondaryPublisherToken": null,
|
||||
"subscriberToken": null
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"name": "publisher connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber connected",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screenshare subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video frames >= 3",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video frame callback",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video stats count >= 1",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber video resolution 640x360",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber aggregate inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats count >= 2",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote microphone subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio frames >= 6",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound microphone stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher local screen-share audio publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber remote screen-share audio subscription",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screen-share audio stats",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber data packet received",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "publisher outbound screenshare codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "subscriber inbound video codec video/VP8",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server publishing API participants listed",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screenshare publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher microphone publication",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"name": "server sees publisher screen-share audio publication",
|
||||
"pass": true
|
||||
}
|
||||
],
|
||||
"skippedReason": null,
|
||||
"error": null,
|
||||
"hardwareEncoder": {
|
||||
"available": false,
|
||||
"backend": "none",
|
||||
"compiled": true,
|
||||
"runtime": false,
|
||||
"codecs": [],
|
||||
"zeroCopy": false,
|
||||
"nativeInputs": [],
|
||||
"reason": "no_supported_devices",
|
||||
"detail": "missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))"
|
||||
},
|
||||
"metrics": {
|
||||
"inboundAudioFrames": 3201,
|
||||
"inboundVideoFrames": 236,
|
||||
"strictAudioFrames": 150,
|
||||
"strictVideoFrames": 221,
|
||||
"videoCallbacks": 235,
|
||||
"videoBytes": 81216000,
|
||||
"receivedFps": 14.71,
|
||||
"requiredFps": 14.25,
|
||||
"maxVideoFrameGapMs": 135,
|
||||
"maxAudioFrameGapMs": 104,
|
||||
"maxVideoFrameGapOverallMs": 135,
|
||||
"maxAudioFrameGapOverallMs": 104,
|
||||
"avDriftMs": 15,
|
||||
"maxObservedPacketLoss": 0,
|
||||
"maxObservedPacketLossDelta": 0,
|
||||
"videoResolutionCounts": {
|
||||
"640x360": 221
|
||||
},
|
||||
"videoResolutionMismatchCount": 0,
|
||||
"droppedVideoFrameCallbacks": 0,
|
||||
"publisherVideoPump": {
|
||||
"input": "bgra",
|
||||
"pattern": "gradient",
|
||||
"targetFps": 15,
|
||||
"startedAtMs": 1780696821343,
|
||||
"stoppedAtMs": null,
|
||||
"framesPushed": 242,
|
||||
"framesRejected": 0,
|
||||
"errors": 0,
|
||||
"maxPushGapMs": 81,
|
||||
"lastPushAtMs": 1780696837414,
|
||||
"elapsedMs": 16181,
|
||||
"producedFps": 14.96
|
||||
},
|
||||
"secondaryPublisherVideoPump": null,
|
||||
"serverPublishing": {
|
||||
"fetchedAtMs": 1780696822415,
|
||||
"error": null,
|
||||
"participants": [
|
||||
{
|
||||
"identity": "fluxer-native-publisher-6872",
|
||||
"trackCount": 3,
|
||||
"tracks": [
|
||||
{
|
||||
"sid": "TR_AMZncs3mMUV8XL",
|
||||
"name": "mic",
|
||||
"kind": "audio",
|
||||
"source": "microphone",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_As8q693K2N6RV2",
|
||||
"name": "screen-audio",
|
||||
"kind": "audio",
|
||||
"source": "screenshareAudio",
|
||||
"mimeType": "audio/red",
|
||||
"muted": false
|
||||
},
|
||||
{
|
||||
"sid": "TR_VScEzJLWokWJcx",
|
||||
"name": "screen",
|
||||
"kind": "video",
|
||||
"source": "screenshare",
|
||||
"mimeType": "video/VP8",
|
||||
"muted": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identity": "fluxer-native-subscriber-6872",
|
||||
"trackCount": 0,
|
||||
"tracks": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastVideoMeta": {
|
||||
"participantSid": "PA_EeCfpcRvJPEF",
|
||||
"trackSid": "TR_VScEzJLWokWJcx",
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"timestampUs": 3450776000
|
||||
},
|
||||
"publisherStats": {
|
||||
"rttMs": 4,
|
||||
"outbound": [
|
||||
{
|
||||
"trackSid": "TR_As8q693K2N6RV2",
|
||||
"source": "screenshareAudio",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 263.5,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_VScEzJLWokWJcx",
|
||||
"source": "screenshare",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1233.4,
|
||||
"packetsLost": 0,
|
||||
"fps": 15,
|
||||
"codec": "video/VP8"
|
||||
},
|
||||
{
|
||||
"trackSid": "TR_AMZncs3mMUV8XL",
|
||||
"source": "microphone",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 97.9,
|
||||
"packetsLost": 0,
|
||||
"codec": "audio/opus"
|
||||
}
|
||||
],
|
||||
"inbound": [],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 1,
|
||||
"outgoingVideoFramesProduced": 241,
|
||||
"outgoingVideoFramesAccepted": 241,
|
||||
"outgoingVideoFramesDropped": 4,
|
||||
"outgoingVideoFramesCoalesced": 4,
|
||||
"outgoingVideoFramesCaptured": 236,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 14.71,
|
||||
"outgoingVideoTargetFps": 15,
|
||||
"outgoingVideoMaxQueueAgeMs": 67,
|
||||
"outgoingVideoMaxPushLatencyMs": 69,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 36,
|
||||
"adaptiveSendTier": "full",
|
||||
"adaptiveSendReason": "stable"
|
||||
}
|
||||
},
|
||||
"secondaryPublisherStats": null,
|
||||
"subscriberStats": {
|
||||
"rttMs": 4,
|
||||
"outbound": [],
|
||||
"inbound": [
|
||||
{
|
||||
"participantSid": "PA_EeCfpcRvJPEF",
|
||||
"trackSid": "TR_As8q693K2N6RV2",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 263.1,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 3,
|
||||
"audioLevel": 0.318,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_EeCfpcRvJPEF",
|
||||
"trackSid": "TR_AMZncs3mMUV8XL",
|
||||
"kind": "audio",
|
||||
"bitrateKbps": 97.8,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 2,
|
||||
"audioLevel": 0.307,
|
||||
"codec": "audio/opus"
|
||||
},
|
||||
{
|
||||
"participantSid": "PA_EeCfpcRvJPEF",
|
||||
"trackSid": "TR_VScEzJLWokWJcx",
|
||||
"kind": "video",
|
||||
"bitrateKbps": 1231.8,
|
||||
"packetsLost": 0,
|
||||
"jitterMs": 4,
|
||||
"codec": "video/VP8"
|
||||
}
|
||||
],
|
||||
"send": {
|
||||
"outgoingVideoQueueDepth": 0,
|
||||
"outgoingVideoFramesProduced": 0,
|
||||
"outgoingVideoFramesAccepted": 0,
|
||||
"outgoingVideoFramesDropped": 0,
|
||||
"outgoingVideoFramesCoalesced": 0,
|
||||
"outgoingVideoFramesCaptured": 0,
|
||||
"outgoingVideoCaptureFailures": 0,
|
||||
"outgoingVideoEffectiveFps": 0,
|
||||
"outgoingVideoTargetFps": 0,
|
||||
"outgoingVideoMaxQueueAgeMs": 0,
|
||||
"outgoingVideoMaxPushLatencyMs": 0,
|
||||
"outgoingAudioBufferTargetMs": 200,
|
||||
"outgoingAudioBufferMaxMs": 750,
|
||||
"outgoingAudioUnderruns": 0,
|
||||
"outgoingAudioRebuffers": 0,
|
||||
"outgoingAudioMaxFrameGapMs": 0,
|
||||
"adaptiveSendTier": "idle",
|
||||
"adaptiveSendReason": "notPublishing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-113
@@ -64,15 +64,6 @@ version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.20.3"
|
||||
@@ -239,16 +230,6 @@ version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||
|
||||
[[package]]
|
||||
name = "dispatch2"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.16.0"
|
||||
@@ -268,10 +249,6 @@ dependencies = [
|
||||
"bindgen",
|
||||
"criterion",
|
||||
"libloading 0.9.0",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-video",
|
||||
"objc2-io-surface",
|
||||
"windows",
|
||||
]
|
||||
|
||||
@@ -405,96 +382,6 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
|
||||
dependencies = [
|
||||
"objc2-encode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-foundation"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"block2",
|
||||
"dispatch2",
|
||||
"libc",
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-graphics"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"dispatch2",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-io-surface",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-video"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"block2",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
"objc2-io-surface",
|
||||
"objc2-metal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-encode"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
|
||||
|
||||
[[package]]
|
||||
name = "objc2-foundation"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-io-surface"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-metal"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
|
||||
@@ -36,12 +36,6 @@ bindgen-sdk = ["dep:bindgen"]
|
||||
[dev-dependencies]
|
||||
criterion = "0.8"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dev-dependencies]
|
||||
objc2 = "0.6"
|
||||
objc2-core-video = {version = "0.3", features = ["CVPixelBuffer", "CVPixelBufferIOSurface", "CVImageBuffer", "CVBuffer", "CVPixelFormatDescription", "CVReturn", "CVBase", "objc2-io-surface"]}
|
||||
objc2-core-foundation = {version = "0.3", features = ["CFDictionary", "CFNumber", "CFString", "CFBase"]}
|
||||
objc2-io-surface = {version = "0.3", features = ["IOSurfaceRef", "objc2-core-foundation"]}
|
||||
|
||||
[[bench]]
|
||||
name = "ring"
|
||||
harness = false
|
||||
|
||||
@@ -438,36 +438,4 @@ mod tests {
|
||||
assert_eq!(h2.slot_index, 3);
|
||||
assert_eq!(h2.raw_handle, 0xdead);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "windows")]
|
||||
fn windows_real_keyed_mutex_eight_slots_round_trip() {
|
||||
let backend_result = D3D11KeyedMutexBackend::new();
|
||||
let mut backend = match backend_result {
|
||||
Ok(b) => b,
|
||||
Err(BackendError::PlatformUnsupported { .. }) => return,
|
||||
Err(other) => unreachable!("unexpected backend init err: {other:?}"),
|
||||
};
|
||||
let slots_result = backend.create_slots(64, 64, TextureFormat::Nv12);
|
||||
let slots = match slots_result {
|
||||
Ok(s) => s,
|
||||
Err(BackendError::PlatformUnsupported { .. }) => return,
|
||||
Err(other) => unreachable!("unexpected create_slots err: {other:?}"),
|
||||
};
|
||||
assert_eq!(slots.len(), 8);
|
||||
for (idx, slot) in slots.iter().enumerate() {
|
||||
assert_eq!(slot.slot_index, idx as u32);
|
||||
assert_ne!(slot.raw_handle, 0);
|
||||
assert_eq!(slot.width, 64);
|
||||
}
|
||||
let slot = slots[0].clone();
|
||||
backend.acquire_write(&slot, 0).expect("acquire key=0");
|
||||
backend.release_write(&slot, 1).expect("release key=1");
|
||||
assert!(backend.poll_complete(&slot));
|
||||
backend.mark_consumed(&slot);
|
||||
assert!(!backend.poll_complete(&slot));
|
||||
backend.acquire_write(&slot, 1).expect("acquire key=1");
|
||||
backend.release_write(&slot, 2).expect("release key=2");
|
||||
assert!(backend.poll_complete(&slot));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,6 @@ type OsStatus = i32;
|
||||
pub(crate) type IoSurfaceRef = *mut c_void;
|
||||
|
||||
const KCM_VIDEO_CODEC_TYPE_H264: u32 = u32::from_be_bytes(*b"avc1");
|
||||
#[cfg(test)]
|
||||
const KCV_PIXEL_FORMAT_TYPE_420_YPCBCR8_BIPLANAR_VIDEO: u32 = u32::from_be_bytes(*b"420v");
|
||||
const NOERR: OsStatus = 0;
|
||||
const KCM_TIME_FLAGS_VALID: u32 = 1;
|
||||
const KCF_NUMBER_SINT32_TYPE: i32 = 3;
|
||||
@@ -840,107 +838,10 @@ impl VideoToolboxHandoff for VtCompressionHandoff {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn make_cv_nv12_pixel_buffer(
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> objc2_core_foundation::CFRetained<objc2_core_video::CVPixelBuffer> {
|
||||
use core::ptr::NonNull;
|
||||
use objc2_core_foundation::{CFDictionary, CFRetained};
|
||||
use objc2_core_video::CVPixelBuffer;
|
||||
|
||||
let mut empty_keys: [*const c_void; 0] = [];
|
||||
let mut empty_vals: [*const c_void; 0] = [];
|
||||
let iosurf_dict: CFRetained<CFDictionary> = unsafe {
|
||||
CFDictionary::new(
|
||||
None,
|
||||
empty_keys.as_mut_ptr(),
|
||||
empty_vals.as_mut_ptr(),
|
||||
0,
|
||||
&objc2_core_foundation::kCFTypeDictionaryKeyCallBacks,
|
||||
&objc2_core_foundation::kCFTypeDictionaryValueCallBacks,
|
||||
)
|
||||
.expect("iosurf empty dict")
|
||||
};
|
||||
let key_ref: &objc2_core_foundation::CFString =
|
||||
unsafe { objc2_core_video::kCVPixelBufferIOSurfacePropertiesKey };
|
||||
let key_ptr: *const c_void = key_ref as *const _ as *const c_void;
|
||||
let val_ptr: *const c_void = &*iosurf_dict as *const _ as *const c_void;
|
||||
let mut keys = [key_ptr];
|
||||
let mut vals = [val_ptr];
|
||||
let attrs: CFRetained<CFDictionary> = unsafe {
|
||||
CFDictionary::new(
|
||||
None,
|
||||
keys.as_mut_ptr(),
|
||||
vals.as_mut_ptr(),
|
||||
1,
|
||||
&objc2_core_foundation::kCFTypeDictionaryKeyCallBacks,
|
||||
&objc2_core_foundation::kCFTypeDictionaryValueCallBacks,
|
||||
)
|
||||
.expect("attrs dict")
|
||||
};
|
||||
let mut out: *mut CVPixelBuffer = std::ptr::null_mut();
|
||||
let status = unsafe {
|
||||
objc2_core_video::CVPixelBufferCreate(
|
||||
None,
|
||||
width as usize,
|
||||
height as usize,
|
||||
KCV_PIXEL_FORMAT_TYPE_420_YPCBCR8_BIPLANAR_VIDEO,
|
||||
Some(&attrs),
|
||||
NonNull::new(&mut out).expect("out pointer non-null"),
|
||||
)
|
||||
};
|
||||
assert_eq!(status, 0, "CVPixelBufferCreate succeeded");
|
||||
assert!(!out.is_null(), "CVPixelBufferCreate produced buffer");
|
||||
unsafe { CFRetained::from_raw(NonNull::new_unchecked(out)) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn make_handoff() -> VtCompressionHandoff {
|
||||
VtCompressionHandoff::try_new(EncoderDims::new(1280, 720)).expect("create handoff")
|
||||
}
|
||||
|
||||
pub(super) fn iosurface_ptr_from_pb(pb: &objc2_core_video::CVPixelBuffer) -> *mut c_void {
|
||||
let s = objc2_core_video::CVPixelBufferGetIOSurface(Some(pb));
|
||||
let surface = s.expect("pixel buffer must be IOSurface-backed");
|
||||
let raw: *const objc2_io_surface::IOSurfaceRef = &*surface;
|
||||
raw as *mut c_void
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vt_compression_handoff_encodes_one_frame() {
|
||||
let mut handoff = make_handoff();
|
||||
let pb = make_cv_nv12_pixel_buffer(1280, 720);
|
||||
let surface = iosurface_ptr_from_pb(&pb);
|
||||
let slot = handoff
|
||||
.register_slot(surface as u64, 0, EncoderDims::new(1280, 720))
|
||||
.expect("register slot");
|
||||
let params = PicParams::new(0, false);
|
||||
handoff
|
||||
.encode_shared_async(slot, 0, EncoderDims::new(1280, 720), params)
|
||||
.expect("encode async");
|
||||
handoff.wait_for_completion().expect("complete frames");
|
||||
let payload = handoff.poll_completed(slot).expect("polled bitstream");
|
||||
assert!(!payload.data.is_empty(), "encoded payload non-empty");
|
||||
assert!(
|
||||
payload.data.len() <= MAX_BITSTREAM_BYTES,
|
||||
"encoded payload bounded"
|
||||
);
|
||||
}
|
||||
|
||||
struct RecordingCallback {
|
||||
seen: Vec<(u64, u32)>,
|
||||
}
|
||||
|
||||
impl crate::encoder_handoff::EncoderCompletionCallback for RecordingCallback {
|
||||
fn on_complete(&mut self, sequence: u64, encoded_bytes: u32) {
|
||||
self.seen.push((sequence, encoded_bytes));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn output_callback_counts_failed_completions() {
|
||||
let ctx = VtCallbackContext {
|
||||
@@ -971,106 +872,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_completion_count_starts_at_zero_and_survives_encode() {
|
||||
let mut handoff = make_handoff();
|
||||
assert_eq!(handoff.failed_completion_count(), 0);
|
||||
let pb = make_cv_nv12_pixel_buffer(1280, 720);
|
||||
let surface = iosurface_ptr_from_pb(&pb);
|
||||
let slot = handoff
|
||||
.register_slot(surface as u64, 0, EncoderDims::new(1280, 720))
|
||||
.expect("register slot");
|
||||
handoff
|
||||
.encode_shared_async(
|
||||
slot,
|
||||
0,
|
||||
EncoderDims::new(1280, 720),
|
||||
PicParams::new(0, false),
|
||||
)
|
||||
.expect("encode async");
|
||||
handoff.wait_for_completion().expect("complete frames");
|
||||
assert_eq!(handoff.failed_completion_count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_rebuild_bounded_by_named_cap() {
|
||||
let mut handoff = make_handoff();
|
||||
assert_eq!(handoff.session_rebuild_count(), 0);
|
||||
for expected in 1..=VT_SESSION_REBUILD_MAX {
|
||||
assert!(handoff.try_rebuild_session(), "rebuild within cap succeeds");
|
||||
assert_eq!(handoff.session_rebuild_count(), expected);
|
||||
}
|
||||
assert!(
|
||||
!handoff.try_rebuild_session(),
|
||||
"rebuild beyond cap rejected"
|
||||
);
|
||||
assert_eq!(handoff.session_rebuild_count(), VT_SESSION_REBUILD_MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_still_encodes_after_rebuild() {
|
||||
let mut handoff = make_handoff();
|
||||
assert!(handoff.try_rebuild_session(), "first rebuild succeeds");
|
||||
let pb = make_cv_nv12_pixel_buffer(1280, 720);
|
||||
let surface = iosurface_ptr_from_pb(&pb);
|
||||
let slot = handoff
|
||||
.register_slot(surface as u64, 0, EncoderDims::new(1280, 720))
|
||||
.expect("register slot");
|
||||
handoff
|
||||
.encode_shared_async(
|
||||
slot,
|
||||
0,
|
||||
EncoderDims::new(1280, 720),
|
||||
PicParams::new(0, false),
|
||||
)
|
||||
.expect("encode async after rebuild");
|
||||
handoff.wait_for_completion().expect("complete frames");
|
||||
let payload = handoff.poll_completed(slot).expect("polled bitstream");
|
||||
assert!(!payload.data.is_empty(), "encoded payload non-empty");
|
||||
assert_eq!(handoff.session_rebuild_count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_shared_uses_capture_pts_when_present() {
|
||||
let mut handoff = make_handoff();
|
||||
let pb = make_cv_nv12_pixel_buffer(1280, 720);
|
||||
let surface = iosurface_ptr_from_pb(&pb);
|
||||
let slot = handoff
|
||||
.register_slot(surface as u64, 0, EncoderDims::new(1280, 720))
|
||||
.expect("register slot");
|
||||
assert_eq!(slot.slot_index, 0);
|
||||
let dims = EncoderDims::new(1280, 720);
|
||||
let submission =
|
||||
EncoderSubmission::new(surface as u64, 0, dims, 0).with_capture_pts_us(987_654);
|
||||
let mut cb = RecordingCallback { seen: Vec::new() };
|
||||
VideoToolboxHandoff::encode_shared(&mut handoff, submission, &mut cb)
|
||||
.expect("encode shared with capture pts");
|
||||
handoff.wait_for_completion().expect("complete frames");
|
||||
let payload = handoff.poll_completed(slot).expect("polled bitstream");
|
||||
assert_eq!(payload.pts_us, 987_654, "real capture pts threaded through");
|
||||
assert_eq!(cb.seen.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_shared_synthesizes_pts_when_capture_pts_absent() {
|
||||
let mut handoff = make_handoff();
|
||||
let pb = make_cv_nv12_pixel_buffer(1280, 720);
|
||||
let surface = iosurface_ptr_from_pb(&pb);
|
||||
let slot = handoff
|
||||
.register_slot(surface as u64, 0, EncoderDims::new(1280, 720))
|
||||
.expect("register slot");
|
||||
assert_eq!(slot.slot_index, 0);
|
||||
let dims = EncoderDims::new(1280, 720);
|
||||
let submission = EncoderSubmission::new(surface as u64, 0, dims, 0);
|
||||
assert_eq!(submission.capture_pts_us, None);
|
||||
let mut cb = RecordingCallback { seen: Vec::new() };
|
||||
VideoToolboxHandoff::encode_shared(&mut handoff, submission, &mut cb)
|
||||
.expect("encode shared without capture pts");
|
||||
handoff.wait_for_completion().expect("complete frames");
|
||||
let payload = handoff.poll_completed(slot).expect("polled bitstream");
|
||||
assert_eq!(payload.pts_us, 0, "sequence 0 synthesizes pts 0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_session_error_detection_is_exact() {
|
||||
let invalid = EncoderError::EncodeFailed {
|
||||
@@ -1089,38 +890,4 @@ mod tests {
|
||||
};
|
||||
assert!(!is_invalid_session_error(&other_vendor));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vt_compression_handoff_handles_back_pressure() {
|
||||
let mut handoff = make_handoff();
|
||||
let pb = make_cv_nv12_pixel_buffer(1280, 720);
|
||||
let surface = iosurface_ptr_from_pb(&pb);
|
||||
let slot = handoff
|
||||
.register_slot(surface as u64, 0, EncoderDims::new(1280, 720))
|
||||
.expect("register slot");
|
||||
let total: u64 = (COMPLETION_RING_CAPACITY as u64) + 8;
|
||||
for i in 0..total {
|
||||
let params = PicParams::new(i * 33_333, false);
|
||||
handoff
|
||||
.encode_shared_async(slot, 0, EncoderDims::new(1280, 720), params)
|
||||
.expect("encode async");
|
||||
}
|
||||
handoff.wait_for_completion().expect("complete frames");
|
||||
let pending = handoff.pending_completion();
|
||||
assert!(
|
||||
pending <= COMPLETION_RING_CAPACITY,
|
||||
"completion ring stays bounded"
|
||||
);
|
||||
let mut drained = 0usize;
|
||||
while handoff.poll_completed(slot).is_some() {
|
||||
drained += 1;
|
||||
if drained > COMPLETION_RING_CAPACITY * 2 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert!(
|
||||
drained <= COMPLETION_RING_CAPACITY,
|
||||
"drained respects completion bound"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,727 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
#![cfg(target_os = "windows")]
|
||||
use std::ffi::c_void;
|
||||
use std::ptr;
|
||||
|
||||
use libloading::{Library, Symbol};
|
||||
use windows::Win32::Graphics::Direct3D::{D3D_DRIVER_TYPE_UNKNOWN, D3D_FEATURE_LEVEL_11_0};
|
||||
use windows::Win32::Graphics::Direct3D11::{
|
||||
D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_CREATE_DEVICE_VIDEO_SUPPORT, D3D11_SDK_VERSION,
|
||||
D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, ID3D11Multithread,
|
||||
};
|
||||
use windows::Win32::Graphics::Dxgi::{CreateDXGIFactory1, IDXGIAdapter1, IDXGIFactory1};
|
||||
use windows::core::Interface;
|
||||
|
||||
const VENDOR_INTEL: u32 = 0x8086;
|
||||
|
||||
const MFX_IMPL_TYPE_HARDWARE: u32 = 2;
|
||||
const MFX_ACCEL_MODE_NA: u32 = 0;
|
||||
const MFX_ACCEL_MODE_VIA_D3D11: u32 = 0x0300;
|
||||
|
||||
const MFX_HANDLE_D3D11_DEVICE: u32 = 3;
|
||||
const MFX_HANDLE_D3D11_VIDEO_DEVICE_GUESS: u32 = 11;
|
||||
|
||||
const MFX_VARIANT_TYPE_U32: u32 = 5;
|
||||
const MFX_VARIANT_VERSION_MAJOR: u8 = 1;
|
||||
const MFX_VARIANT_VERSION_MINOR: u8 = 1;
|
||||
|
||||
const MFX_ERR_NONE: i32 = 0;
|
||||
const MFX_WRN_IN_EXECUTION: i32 = 1;
|
||||
|
||||
const MFX_FOURCC_NV12: u32 = u32::from_le_bytes(*b"NV12");
|
||||
const MFX_CODEC_AVC: u32 = u32::from_le_bytes(*b"AVC ");
|
||||
const MFX_RATECONTROL_CBR: u16 = 1;
|
||||
const MFX_PICSTRUCT_PROGRESSIVE: u16 = 0x01;
|
||||
const MFX_CHROMAFORMAT_YUV420: u16 = 1;
|
||||
const MFX_IOPATTERN_IN_VIDEO_MEMORY: u16 = 0x01;
|
||||
const MFX_IOPATTERN_IN_SYSTEM_MEMORY: u16 = 0x02;
|
||||
|
||||
const FILTER_PROPERTY_IMPL: &[u8] = b"mfxImplDescription.Impl\0";
|
||||
const FILTER_PROPERTY_ACCEL: &[u8] = b"mfxImplDescription.AccelerationMode\0";
|
||||
|
||||
const QSV_DLL_NAME_VPL: &str = "libvpl.dll";
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
struct MfxStructVersion {
|
||||
minor: u8,
|
||||
major: u8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
union MfxVariantData {
|
||||
u32_: u32,
|
||||
u64_: u64,
|
||||
ptr: *mut c_void,
|
||||
pad: [u8; 16],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
struct MfxVariant {
|
||||
version: MfxStructVersion,
|
||||
type_: u32,
|
||||
data: MfxVariantData,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
struct MfxFrameInfo {
|
||||
reserved: [u32; 4],
|
||||
channel_id: u16,
|
||||
bit_depth_luma: u16,
|
||||
bit_depth_chroma: u16,
|
||||
shift: u16,
|
||||
frame_id_temporal: u16,
|
||||
frame_id_priority: u16,
|
||||
frame_id_view: u16,
|
||||
frame_id_quality: u16,
|
||||
four_cc: u32,
|
||||
width: u16,
|
||||
height: u16,
|
||||
crop_x: u16,
|
||||
crop_y: u16,
|
||||
crop_w: u16,
|
||||
crop_h: u16,
|
||||
frame_rate_extn: u32,
|
||||
frame_rate_extd: u32,
|
||||
reserved3: u16,
|
||||
aspect_ratio_w: u16,
|
||||
aspect_ratio_h: u16,
|
||||
pic_struct: u16,
|
||||
chroma_format: u16,
|
||||
reserved2: u16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
struct MfxInfoMfx {
|
||||
reserved: [u32; 7],
|
||||
low_power: u16,
|
||||
brc_param_multiplier: u16,
|
||||
frame_info: MfxFrameInfo,
|
||||
codec_id: u32,
|
||||
codec_profile: u16,
|
||||
codec_level: u16,
|
||||
num_thread: u16,
|
||||
target_usage: u16,
|
||||
gop_pic_size: u16,
|
||||
gop_ref_dist: u16,
|
||||
gop_opt_flag: u16,
|
||||
idr_interval: u16,
|
||||
rate_control_method: u16,
|
||||
init_qp: u16,
|
||||
buffer_size_in_kb: u16,
|
||||
target_kbps: u16,
|
||||
max_kbps: u16,
|
||||
num_slice: u16,
|
||||
num_ref_frame: u16,
|
||||
encoded_order: u16,
|
||||
union_pad: [u16; 15],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct MfxVideoParam {
|
||||
alloc_id: u32,
|
||||
reserved: [u32; 2],
|
||||
reserved3: u16,
|
||||
async_depth: u16,
|
||||
mfx: MfxInfoMfx,
|
||||
protected: u16,
|
||||
io_pattern: u16,
|
||||
ext_param: *mut c_void,
|
||||
num_ext_param: u16,
|
||||
reserved2: u16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy)]
|
||||
struct MfxPlatform {
|
||||
code_name: u16,
|
||||
device_id: u16,
|
||||
media_adapter_type: u16,
|
||||
reserved: [u16; 13],
|
||||
}
|
||||
|
||||
type MfxLoad = unsafe extern "C" fn() -> *mut c_void;
|
||||
type MfxUnload = unsafe extern "C" fn(*mut c_void);
|
||||
type MfxCreateConfig = unsafe extern "C" fn(*mut c_void) -> *mut c_void;
|
||||
type MfxSetConfigFilterProperty = unsafe extern "C" fn(*mut c_void, *const u8, MfxVariant) -> i32;
|
||||
type MfxCreateSession = unsafe extern "C" fn(*mut c_void, u32, *mut *mut c_void) -> i32;
|
||||
type MfxClose = unsafe extern "C" fn(*mut c_void) -> i32;
|
||||
type MfxSetHandle = unsafe extern "C" fn(*mut c_void, u32, *mut c_void) -> i32;
|
||||
type MfxQueryImpl = unsafe extern "C" fn(*mut c_void, *mut i32) -> i32;
|
||||
type MfxQueryPlatform = unsafe extern "C" fn(*mut c_void, *mut MfxPlatform) -> i32;
|
||||
type MfxEncodeInit = unsafe extern "C" fn(*mut c_void, *mut MfxVideoParam) -> i32;
|
||||
type MfxEncodeClose = unsafe extern "C" fn(*mut c_void) -> i32;
|
||||
type MfxEncodeQuery =
|
||||
unsafe extern "C" fn(*mut c_void, *mut MfxVideoParam, *mut MfxVideoParam) -> i32;
|
||||
type MfxGetHandle = unsafe extern "C" fn(*mut c_void, u32, *mut *mut c_void) -> i32;
|
||||
|
||||
struct Dispatcher {
|
||||
library: Library,
|
||||
}
|
||||
|
||||
impl Dispatcher {
|
||||
fn load() -> Option<Self> {
|
||||
let library = unsafe { Library::new(QSV_DLL_NAME_VPL) }.ok()?;
|
||||
Some(Self { library })
|
||||
}
|
||||
|
||||
fn modern_session(&self, accel_mode: u32) -> (Option<(*mut c_void, *mut c_void)>, Vec<String>) {
|
||||
let mut log = Vec::new();
|
||||
let load: Symbol<'_, MfxLoad> = match unsafe { self.library.get(b"MFXLoad\0") } {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
log.push(format!("MFXLoad symbol missing: {e:?}"));
|
||||
return (None, log);
|
||||
}
|
||||
};
|
||||
let create_config: Symbol<'_, MfxCreateConfig> =
|
||||
match unsafe { self.library.get(b"MFXCreateConfig\0") } {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
log.push(format!("MFXCreateConfig missing: {e:?}"));
|
||||
return (None, log);
|
||||
}
|
||||
};
|
||||
let set_prop: Symbol<'_, MfxSetConfigFilterProperty> =
|
||||
match unsafe { self.library.get(b"MFXSetConfigFilterProperty\0") } {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
log.push(format!("MFXSetConfigFilterProperty missing: {e:?}"));
|
||||
return (None, log);
|
||||
}
|
||||
};
|
||||
let create_session: Symbol<'_, MfxCreateSession> =
|
||||
match unsafe { self.library.get(b"MFXCreateSession\0") } {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
log.push(format!("MFXCreateSession missing: {e:?}"));
|
||||
return (None, log);
|
||||
}
|
||||
};
|
||||
let loader = unsafe { load() };
|
||||
if loader.is_null() {
|
||||
log.push("MFXLoad returned NULL".to_string());
|
||||
return (None, log);
|
||||
}
|
||||
log.push(format!("MFXLoad ok loader={loader:p}"));
|
||||
let impl_cfg = unsafe { create_config(loader) };
|
||||
if impl_cfg.is_null() {
|
||||
log.push("MFXCreateConfig(impl) returned NULL".to_string());
|
||||
return (None, log);
|
||||
}
|
||||
let impl_variant = MfxVariant {
|
||||
version: MfxStructVersion {
|
||||
minor: MFX_VARIANT_VERSION_MINOR,
|
||||
major: MFX_VARIANT_VERSION_MAJOR,
|
||||
},
|
||||
type_: MFX_VARIANT_TYPE_U32,
|
||||
data: MfxVariantData {
|
||||
u32_: MFX_IMPL_TYPE_HARDWARE,
|
||||
},
|
||||
};
|
||||
let status_impl =
|
||||
unsafe { set_prop(impl_cfg, FILTER_PROPERTY_IMPL.as_ptr(), impl_variant) };
|
||||
log.push(format!(
|
||||
"SetConfigFilterProperty(Impl=HARDWARE) status={status_impl}"
|
||||
));
|
||||
if status_impl != MFX_ERR_NONE {
|
||||
return (None, log);
|
||||
}
|
||||
let accel_cfg = unsafe { create_config(loader) };
|
||||
if accel_cfg.is_null() {
|
||||
log.push("MFXCreateConfig(accel) returned NULL".to_string());
|
||||
return (None, log);
|
||||
}
|
||||
let accel_variant = MfxVariant {
|
||||
version: MfxStructVersion {
|
||||
minor: MFX_VARIANT_VERSION_MINOR,
|
||||
major: MFX_VARIANT_VERSION_MAJOR,
|
||||
},
|
||||
type_: MFX_VARIANT_TYPE_U32,
|
||||
data: MfxVariantData { u32_: accel_mode },
|
||||
};
|
||||
let status_accel =
|
||||
unsafe { set_prop(accel_cfg, FILTER_PROPERTY_ACCEL.as_ptr(), accel_variant) };
|
||||
log.push(format!(
|
||||
"SetConfigFilterProperty(AccelerationMode={accel_mode:#x}) status={status_accel}"
|
||||
));
|
||||
if status_accel != MFX_ERR_NONE {
|
||||
return (None, log);
|
||||
}
|
||||
let mut session: *mut c_void = ptr::null_mut();
|
||||
let status_create = unsafe { create_session(loader, 0, &mut session) };
|
||||
log.push(format!(
|
||||
"MFXCreateSession status={status_create} session={session:p}"
|
||||
));
|
||||
if status_create != MFX_ERR_NONE || session.is_null() {
|
||||
return (None, log);
|
||||
}
|
||||
(Some((loader, session)), log)
|
||||
}
|
||||
|
||||
fn unload(&self, loader: *mut c_void) {
|
||||
let Ok(unload) = (unsafe { self.library.get::<MfxUnload>(b"MFXUnload\0") }) else {
|
||||
return;
|
||||
};
|
||||
if !loader.is_null() {
|
||||
unsafe { unload(loader) };
|
||||
}
|
||||
}
|
||||
|
||||
fn close(&self, session: *mut c_void) {
|
||||
let Ok(close) = (unsafe { self.library.get::<MfxClose>(b"MFXClose\0") }) else {
|
||||
return;
|
||||
};
|
||||
if !session.is_null() {
|
||||
unsafe {
|
||||
let _ = close(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_create_intel_device() -> Option<(ID3D11Device, ID3D11DeviceContext)> {
|
||||
let factory: IDXGIFactory1 = unsafe { CreateDXGIFactory1() }.ok()?;
|
||||
let mut idx: u32 = 0;
|
||||
loop {
|
||||
let adapter: IDXGIAdapter1 = match unsafe { factory.EnumAdapters1(idx) } {
|
||||
Ok(a) => a,
|
||||
Err(_) => return None,
|
||||
};
|
||||
idx = idx.saturating_add(1);
|
||||
let desc = match unsafe { adapter.GetDesc1() } {
|
||||
Ok(d) => d,
|
||||
Err(_) => continue,
|
||||
};
|
||||
if desc.VendorId != VENDOR_INTEL {
|
||||
continue;
|
||||
}
|
||||
let mut device: Option<ID3D11Device> = None;
|
||||
let mut ctx: Option<ID3D11DeviceContext> = None;
|
||||
let feature_levels = [D3D_FEATURE_LEVEL_11_0];
|
||||
let cast_result = adapter.cast::<windows::Win32::Graphics::Dxgi::IDXGIAdapter>();
|
||||
let cast_adapter = match cast_result {
|
||||
Ok(a) => a,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
|
||||
let result = unsafe {
|
||||
D3D11CreateDevice(
|
||||
Some(&cast_adapter),
|
||||
D3D_DRIVER_TYPE_UNKNOWN,
|
||||
Default::default(),
|
||||
flags,
|
||||
Some(&feature_levels),
|
||||
D3D11_SDK_VERSION,
|
||||
Some(&mut device),
|
||||
None,
|
||||
Some(&mut ctx),
|
||||
)
|
||||
};
|
||||
if let (Ok(()), Some(d), Some(c)) = (result, device, ctx) {
|
||||
if let Ok(mt) = d.cast::<ID3D11Multithread>() {
|
||||
let _ = unsafe { mt.SetMultithreadProtected(true) };
|
||||
}
|
||||
return Some((d, c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_video_params_with(
|
||||
width: u16,
|
||||
height: u16,
|
||||
io_pattern: u16,
|
||||
target_usage: u16,
|
||||
low_power: u16,
|
||||
) -> MfxVideoParam {
|
||||
let aligned_w = (width + 15) & !15;
|
||||
let aligned_h = (height + 15) & !15;
|
||||
let info = MfxFrameInfo {
|
||||
four_cc: MFX_FOURCC_NV12,
|
||||
width: aligned_w,
|
||||
height: aligned_h,
|
||||
crop_w: width,
|
||||
crop_h: height,
|
||||
frame_rate_extn: 30,
|
||||
frame_rate_extd: 1,
|
||||
aspect_ratio_w: 1,
|
||||
aspect_ratio_h: 1,
|
||||
pic_struct: MFX_PICSTRUCT_PROGRESSIVE,
|
||||
chroma_format: MFX_CHROMAFORMAT_YUV420,
|
||||
..Default::default()
|
||||
};
|
||||
let mfx = MfxInfoMfx {
|
||||
frame_info: info,
|
||||
codec_id: MFX_CODEC_AVC,
|
||||
target_usage,
|
||||
low_power,
|
||||
gop_pic_size: 30,
|
||||
gop_ref_dist: 1,
|
||||
rate_control_method: MFX_RATECONTROL_CBR,
|
||||
target_kbps: 5_000,
|
||||
max_kbps: 5_000,
|
||||
num_slice: 1,
|
||||
num_ref_frame: 1,
|
||||
..Default::default()
|
||||
};
|
||||
MfxVideoParam {
|
||||
alloc_id: 0,
|
||||
reserved: [0; 2],
|
||||
reserved3: 0,
|
||||
async_depth: 1,
|
||||
mfx,
|
||||
protected: 0,
|
||||
io_pattern,
|
||||
ext_param: ptr::null_mut(),
|
||||
num_ext_param: 0,
|
||||
reserved2: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn try_set_handle(
|
||||
dispatcher: &Dispatcher,
|
||||
session: *mut c_void,
|
||||
handle_type: u32,
|
||||
handle_ptr: *mut c_void,
|
||||
) -> Result<i32, String> {
|
||||
let sym: Symbol<'_, MfxSetHandle> =
|
||||
unsafe { dispatcher.library.get(b"MFXVideoCORE_SetHandle\0") }
|
||||
.map_err(|e| format!("MFXVideoCORE_SetHandle missing: {e:?}"))?;
|
||||
let status = unsafe { sym(session, handle_type, handle_ptr) };
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
fn try_query_impl(dispatcher: &Dispatcher, session: *mut c_void) -> Result<(i32, i32), String> {
|
||||
let sym: Symbol<'_, MfxQueryImpl> = unsafe { dispatcher.library.get(b"MFXQueryIMPL\0") }
|
||||
.map_err(|e| format!("MFXQueryIMPL missing: {e:?}"))?;
|
||||
let mut out: i32 = 0;
|
||||
let status = unsafe { sym(session, &mut out) };
|
||||
Ok((status, out))
|
||||
}
|
||||
|
||||
fn try_query_platform(
|
||||
dispatcher: &Dispatcher,
|
||||
session: *mut c_void,
|
||||
) -> Result<(i32, MfxPlatform), String> {
|
||||
let sym: Symbol<'_, MfxQueryPlatform> =
|
||||
unsafe { dispatcher.library.get(b"MFXVideoCORE_QueryPlatform\0") }
|
||||
.map_err(|e| format!("MFXVideoCORE_QueryPlatform missing: {e:?}"))?;
|
||||
let mut p = MfxPlatform::default();
|
||||
let status = unsafe { sym(session, &mut p) };
|
||||
Ok((status, p))
|
||||
}
|
||||
|
||||
fn try_encode_init(
|
||||
dispatcher: &Dispatcher,
|
||||
session: *mut c_void,
|
||||
width: u16,
|
||||
height: u16,
|
||||
io_pattern: u16,
|
||||
) -> Result<i32, String> {
|
||||
try_encode_init_with(dispatcher, session, width, height, io_pattern, 4, 0)
|
||||
}
|
||||
|
||||
fn try_encode_init_with(
|
||||
dispatcher: &Dispatcher,
|
||||
session: *mut c_void,
|
||||
width: u16,
|
||||
height: u16,
|
||||
io_pattern: u16,
|
||||
target_usage: u16,
|
||||
low_power: u16,
|
||||
) -> Result<i32, String> {
|
||||
let query: Symbol<'_, MfxEncodeQuery> =
|
||||
unsafe { dispatcher.library.get(b"MFXVideoENCODE_Query\0") }
|
||||
.map_err(|e| format!("MFXVideoENCODE_Query missing: {e:?}"))?;
|
||||
let init: Symbol<'_, MfxEncodeInit> =
|
||||
unsafe { dispatcher.library.get(b"MFXVideoENCODE_Init\0") }
|
||||
.map_err(|e| format!("MFXVideoENCODE_Init missing: {e:?}"))?;
|
||||
let mut params = build_video_params_with(width, height, io_pattern, target_usage, low_power);
|
||||
let mut query_out = build_video_params_with(width, height, io_pattern, target_usage, low_power);
|
||||
let q_status = unsafe { query(session, &mut params, &mut query_out) };
|
||||
eprintln!(
|
||||
" MFXVideoENCODE_Query(io={io_pattern:#x},tu={target_usage},lp={low_power}) status={q_status}"
|
||||
);
|
||||
if q_status != MFX_ERR_NONE && q_status != MFX_WRN_IN_EXECUTION && q_status != -3 {
|
||||
eprintln!(" Query returned hard error {q_status}; skipping Init to avoid AV");
|
||||
return Ok(q_status);
|
||||
}
|
||||
let init_status = unsafe { init(session, &mut query_out) };
|
||||
Ok(init_status)
|
||||
}
|
||||
|
||||
fn try_get_handle(
|
||||
dispatcher: &Dispatcher,
|
||||
session: *mut c_void,
|
||||
handle_type: u32,
|
||||
) -> Result<(i32, *mut c_void), String> {
|
||||
let sym: Symbol<'_, MfxGetHandle> =
|
||||
unsafe { dispatcher.library.get(b"MFXVideoCORE_GetHandle\0") }
|
||||
.map_err(|e| format!("MFXVideoCORE_GetHandle missing: {e:?}"))?;
|
||||
let mut out: *mut c_void = ptr::null_mut();
|
||||
let status = unsafe { sym(session, handle_type, &mut out) };
|
||||
Ok((status, out))
|
||||
}
|
||||
|
||||
fn try_encode_close(dispatcher: &Dispatcher, session: *mut c_void) {
|
||||
if let Ok(sym) = unsafe {
|
||||
dispatcher
|
||||
.library
|
||||
.get::<MfxEncodeClose>(b"MFXVideoENCODE_Close\0")
|
||||
} {
|
||||
unsafe {
|
||||
let _ = sym(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn qsv_tiger_lake_probe_struct_sizes() {
|
||||
eprintln!("=== STRUCT SIZES (Rust) ===");
|
||||
eprintln!("MfxFrameInfo = {}", std::mem::size_of::<MfxFrameInfo>());
|
||||
eprintln!("MfxInfoMfx = {}", std::mem::size_of::<MfxInfoMfx>());
|
||||
eprintln!("MfxVideoParam = {}", std::mem::size_of::<MfxVideoParam>());
|
||||
assert!(
|
||||
std::mem::size_of::<MfxFrameInfo>() == 68,
|
||||
"MfxFrameInfo size must be 68"
|
||||
);
|
||||
assert!(
|
||||
std::mem::size_of::<MfxInfoMfx>() >= 168,
|
||||
"MfxInfoMfx >= 168 (= sizeof mfxInfoVPP)"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn qsv_tiger_lake_probe_experiment_1_preconditions() {
|
||||
let dispatcher = match Dispatcher::load() {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
eprintln!("skip: libvpl.dll not loadable");
|
||||
return;
|
||||
}
|
||||
};
|
||||
eprintln!("=== EXPERIMENT 1: preconditions (QueryIMPL + QueryPlatform) ===");
|
||||
let (session_opt, log) = dispatcher.modern_session(MFX_ACCEL_MODE_VIA_D3D11);
|
||||
for line in &log {
|
||||
eprintln!(" {line}");
|
||||
}
|
||||
let (loader, session) = match session_opt {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
eprintln!("EXP1 result: modern session not created; cannot probe preconditions");
|
||||
return;
|
||||
}
|
||||
};
|
||||
match try_query_impl(&dispatcher, session) {
|
||||
Ok((status, impl_val)) => {
|
||||
eprintln!(" MFXQueryIMPL: status={status} impl={impl_val:#x}");
|
||||
}
|
||||
Err(e) => eprintln!(" MFXQueryIMPL error: {e}"),
|
||||
}
|
||||
match try_query_platform(&dispatcher, session) {
|
||||
Ok((status, p)) => {
|
||||
eprintln!(
|
||||
" MFXVideoCORE_QueryPlatform: status={status} code_name={} device_id={:#x} media_adapter_type={}",
|
||||
p.code_name, p.device_id, p.media_adapter_type
|
||||
);
|
||||
}
|
||||
Err(e) => eprintln!(" MFXVideoCORE_QueryPlatform error: {e}"),
|
||||
}
|
||||
dispatcher.close(session);
|
||||
dispatcher.unload(loader);
|
||||
eprintln!("EXP1 done");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn qsv_tiger_lake_probe_experiment_2_variant_accel_modes() {
|
||||
let dispatcher = match Dispatcher::load() {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
eprintln!("skip: libvpl.dll not loadable");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let (device, _ctx) = match try_create_intel_device() {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
eprintln!("skip: no Intel D3D11 device");
|
||||
return;
|
||||
}
|
||||
};
|
||||
eprintln!("=== EXPERIMENT 2: SetHandle handle-type variants ===");
|
||||
let (session_opt, log) = dispatcher.modern_session(MFX_ACCEL_MODE_VIA_D3D11);
|
||||
for line in &log {
|
||||
eprintln!(" {line}");
|
||||
}
|
||||
let (loader, session) = match session_opt {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
eprintln!("EXP2 result: modern session not created");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let raw_device = device.as_raw();
|
||||
let exp2a = try_set_handle(&dispatcher, session, MFX_HANDLE_D3D11_DEVICE, raw_device);
|
||||
eprintln!(
|
||||
" EXP2a: SetHandle(MFX_HANDLE_D3D11_DEVICE=3) -> {:?}",
|
||||
exp2a
|
||||
);
|
||||
let exp2b = try_set_handle(
|
||||
&dispatcher,
|
||||
session,
|
||||
MFX_HANDLE_D3D11_VIDEO_DEVICE_GUESS,
|
||||
raw_device,
|
||||
);
|
||||
eprintln!(
|
||||
" EXP2b: SetHandle(MFX_HANDLE_D3D11_VIDEO_DEVICE_GUESS=11) -> {:?}",
|
||||
exp2b
|
||||
);
|
||||
dispatcher.close(session);
|
||||
dispatcher.unload(loader);
|
||||
eprintln!("EXP2 done");
|
||||
assert!(exp2a.is_ok(), "MFXVideoCORE_SetHandle symbol resolves");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn qsv_tiger_lake_probe_experiment_3_skip_set_handle() {
|
||||
let dispatcher = match Dispatcher::load() {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
eprintln!("skip: libvpl.dll not loadable");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let (device, _ctx) = match try_create_intel_device() {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
eprintln!("skip: no Intel D3D11 device");
|
||||
return;
|
||||
}
|
||||
};
|
||||
eprintln!("=== EXPERIMENT 3: SetHandle with VIDEO_SUPPORT device + encoder init ===");
|
||||
let (session_opt, log) = dispatcher.modern_session(MFX_ACCEL_MODE_VIA_D3D11);
|
||||
for line in &log {
|
||||
eprintln!(" {line}");
|
||||
}
|
||||
let (loader, session) = match session_opt {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
eprintln!("EXP3 result: modern session not created");
|
||||
return;
|
||||
}
|
||||
};
|
||||
match try_query_impl(&dispatcher, session) {
|
||||
Ok((status, impl_val)) => {
|
||||
eprintln!(" pre-set MFXQueryIMPL: status={status} impl={impl_val:#x}");
|
||||
}
|
||||
Err(e) => eprintln!(" MFXQueryIMPL error: {e}"),
|
||||
}
|
||||
let raw_device = device.as_raw();
|
||||
let set_status = try_set_handle(&dispatcher, session, MFX_HANDLE_D3D11_DEVICE, raw_device);
|
||||
eprintln!(
|
||||
" EXP3-set: SetHandle(D3D11_DEVICE=3, VIDEO_SUPPORT+BGRA, multithread-protected) -> {:?}",
|
||||
set_status
|
||||
);
|
||||
match try_get_handle(&dispatcher, session, MFX_HANDLE_D3D11_DEVICE) {
|
||||
Ok((status, hdl)) => {
|
||||
eprintln!(
|
||||
" post-set GetHandle(MFX_HANDLE_D3D11_DEVICE) -> status={status} handle={hdl:p}"
|
||||
);
|
||||
}
|
||||
Err(e) => eprintln!(" GetHandle error: {e}"),
|
||||
}
|
||||
let init_a = try_encode_init(
|
||||
&dispatcher,
|
||||
session,
|
||||
1280,
|
||||
720,
|
||||
MFX_IOPATTERN_IN_VIDEO_MEMORY,
|
||||
);
|
||||
eprintln!(
|
||||
" EXP3a (IO_VIDEO_MEMORY, target_usage=4, low_power=0): init -> {:?}",
|
||||
init_a
|
||||
);
|
||||
let init_b = try_encode_init_with(
|
||||
&dispatcher,
|
||||
session,
|
||||
1280,
|
||||
720,
|
||||
MFX_IOPATTERN_IN_VIDEO_MEMORY,
|
||||
7,
|
||||
0x10,
|
||||
);
|
||||
eprintln!(
|
||||
" EXP3b (IO_VIDEO_MEMORY, target_usage=7 best-speed, low_power=ON=0x10): init -> {:?}",
|
||||
init_b
|
||||
);
|
||||
let init_c = try_encode_init_with(
|
||||
&dispatcher,
|
||||
session,
|
||||
1280,
|
||||
720,
|
||||
MFX_IOPATTERN_IN_VIDEO_MEMORY,
|
||||
4,
|
||||
0x10,
|
||||
);
|
||||
eprintln!(
|
||||
" EXP3c (IO_VIDEO_MEMORY, target_usage=4 balanced, low_power=ON=0x10): init -> {:?}",
|
||||
init_c
|
||||
);
|
||||
try_encode_close(&dispatcher, session);
|
||||
dispatcher.close(session);
|
||||
dispatcher.unload(loader);
|
||||
eprintln!("EXP3 done");
|
||||
assert!(init_a.is_ok(), "init_a symbol resolved");
|
||||
assert!(init_b.is_ok(), "init_b symbol resolved");
|
||||
assert!(init_c.is_ok(), "init_c symbol resolved");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn qsv_tiger_lake_probe_experiment_4_software_fallback() {
|
||||
let dispatcher = match Dispatcher::load() {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
eprintln!("skip: libvpl.dll not loadable");
|
||||
return;
|
||||
}
|
||||
};
|
||||
eprintln!("=== EXPERIMENT 4: software-only (ACCEL_MODE_NA) session ===");
|
||||
let (session_opt, log) = dispatcher.modern_session(MFX_ACCEL_MODE_NA);
|
||||
for line in &log {
|
||||
eprintln!(" {line}");
|
||||
}
|
||||
let (loader, session) = match session_opt {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
eprintln!("EXP4 result: software session not created");
|
||||
return;
|
||||
}
|
||||
};
|
||||
match try_query_impl(&dispatcher, session) {
|
||||
Ok((status, impl_val)) => {
|
||||
eprintln!(" MFXQueryIMPL: status={status} impl={impl_val:#x}");
|
||||
}
|
||||
Err(e) => eprintln!(" MFXQueryIMPL error: {e}"),
|
||||
}
|
||||
let init_status = try_encode_init(
|
||||
&dispatcher,
|
||||
session,
|
||||
1280,
|
||||
720,
|
||||
MFX_IOPATTERN_IN_SYSTEM_MEMORY,
|
||||
);
|
||||
eprintln!(" EXP4: MFXVideoENCODE_Init software -> {:?}", init_status);
|
||||
try_encode_close(&dispatcher, session);
|
||||
dispatcher.close(session);
|
||||
dispatcher.unload(loader);
|
||||
eprintln!("EXP4 done");
|
||||
assert!(
|
||||
init_status.is_ok(),
|
||||
"encode init symbol resolved in software"
|
||||
);
|
||||
}
|
||||
@@ -1,551 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
#![cfg(target_os = "windows")]
|
||||
use fluxer_encoder_ring::NVENC_COMPLETION_RING_CAPACITY;
|
||||
use fluxer_encoder_ring::encoder_handoff::EncoderCompletionCallback;
|
||||
use fluxer_encoder_ring::{
|
||||
AmfD3D11Handoff, AmfHandoff, EncodedBitstream, EncoderDims, EncoderError, EncoderSubmission,
|
||||
NvencD3D11Handoff, NvencHandoff, PicParams, QsvD3D11Handoff, QsvHandoff, RingError,
|
||||
};
|
||||
use windows::Win32::Graphics::Direct3D::{D3D_DRIVER_TYPE_UNKNOWN, D3D_FEATURE_LEVEL_11_0};
|
||||
use windows::Win32::Graphics::Direct3D11::{
|
||||
D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE, D3D11_CREATE_DEVICE_BGRA_SUPPORT,
|
||||
D3D11_CREATE_DEVICE_VIDEO_SUPPORT, D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX,
|
||||
D3D11_RESOURCE_MISC_SHARED_NTHANDLE, D3D11_SDK_VERSION, D3D11_TEXTURE2D_DESC,
|
||||
D3D11_USAGE_DEFAULT, D3D11CreateDevice, ID3D11Device, ID3D11Multithread,
|
||||
};
|
||||
use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT_NV12, DXGI_SAMPLE_DESC};
|
||||
use windows::Win32::Graphics::Dxgi::{
|
||||
CreateDXGIFactory1, IDXGIAdapter1, IDXGIFactory1, IDXGIResource1,
|
||||
};
|
||||
use windows::core::Interface;
|
||||
|
||||
const VENDOR_NVIDIA: u32 = 0x10DE;
|
||||
const VENDOR_AMD: u32 = 0x1002;
|
||||
const VENDOR_INTEL: u32 = 0x8086;
|
||||
|
||||
fn enum_adapters() -> Vec<IDXGIAdapter1> {
|
||||
let factory_result: windows::core::Result<IDXGIFactory1> = unsafe { CreateDXGIFactory1() };
|
||||
let factory = match factory_result {
|
||||
Ok(f) => f,
|
||||
Err(_) => return Vec::new(),
|
||||
};
|
||||
let mut out = Vec::new();
|
||||
let mut index: u32 = 0;
|
||||
loop {
|
||||
let result: windows::core::Result<IDXGIAdapter1> = unsafe { factory.EnumAdapters1(index) };
|
||||
match result {
|
||||
Ok(adapter) => {
|
||||
out.push(adapter);
|
||||
index = index.saturating_add(1);
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn try_create_device_for_vendor(vendor_id: u32) -> Option<ID3D11Device> {
|
||||
let adapters = enum_adapters();
|
||||
for adapter in adapters {
|
||||
let desc = match unsafe { adapter.GetDesc1() } {
|
||||
Ok(d) => d,
|
||||
Err(_) => continue,
|
||||
};
|
||||
if desc.VendorId != vendor_id {
|
||||
continue;
|
||||
}
|
||||
let mut device: Option<ID3D11Device> = None;
|
||||
let feature_levels = [D3D_FEATURE_LEVEL_11_0];
|
||||
let flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
|
||||
let result = unsafe {
|
||||
D3D11CreateDevice(
|
||||
Some(&adapter.cast().ok()?),
|
||||
D3D_DRIVER_TYPE_UNKNOWN,
|
||||
Default::default(),
|
||||
flags,
|
||||
Some(&feature_levels),
|
||||
D3D11_SDK_VERSION,
|
||||
Some(&mut device),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
};
|
||||
if let (Ok(()), Some(d)) = (result, device) {
|
||||
if let Ok(mt) = d.cast::<ID3D11Multithread>() {
|
||||
let _ = unsafe { mt.SetMultithreadProtected(true) };
|
||||
}
|
||||
return Some(d);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn try_create_device() -> Option<ID3D11Device> {
|
||||
try_create_device_for_vendor(VENDOR_NVIDIA)
|
||||
.or_else(|| try_create_device_for_vendor(VENDOR_INTEL))
|
||||
.or_else(|| try_create_device_for_vendor(VENDOR_AMD))
|
||||
}
|
||||
|
||||
fn try_create_shared_nv12(device: &ID3D11Device, width: u32, height: u32) -> Option<u64> {
|
||||
let desc = D3D11_TEXTURE2D_DESC {
|
||||
Width: width,
|
||||
Height: height,
|
||||
MipLevels: 1,
|
||||
ArraySize: 1,
|
||||
Format: DXGI_FORMAT_NV12,
|
||||
SampleDesc: DXGI_SAMPLE_DESC {
|
||||
Count: 1,
|
||||
Quality: 0,
|
||||
},
|
||||
Usage: D3D11_USAGE_DEFAULT,
|
||||
BindFlags: (D3D11_BIND_RENDER_TARGET.0 | D3D11_BIND_SHADER_RESOURCE.0) as u32,
|
||||
CPUAccessFlags: 0,
|
||||
MiscFlags: (D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX.0 | D3D11_RESOURCE_MISC_SHARED_NTHANDLE.0)
|
||||
as u32,
|
||||
};
|
||||
let mut texture = None;
|
||||
unsafe {
|
||||
device
|
||||
.CreateTexture2D(&desc, None, Some(&mut texture))
|
||||
.ok()?;
|
||||
}
|
||||
let texture = texture?;
|
||||
let resource: IDXGIResource1 = texture.cast().ok()?;
|
||||
let shared = unsafe {
|
||||
resource
|
||||
.CreateSharedHandle(None, 0x3, windows::core::PCWSTR::null())
|
||||
.ok()?
|
||||
};
|
||||
Some(shared.0 as u64)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nvenc_real_encode_one_frame_via_dummy_shared_texture() {
|
||||
let device = match try_create_device_for_vendor(VENDOR_NVIDIA) {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
eprintln!("skip: no NVIDIA D3D11 device available");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let dims = EncoderDims::new(1920, 1080);
|
||||
let handoff_result = NvencD3D11Handoff::new(device.clone(), dims, 5_000_000);
|
||||
let mut handoff = match handoff_result {
|
||||
Ok(h) => h,
|
||||
Err(EncoderError::SdkNotFound { .. }) => {
|
||||
eprintln!("skip: NVENC runtime DLL not available");
|
||||
return;
|
||||
}
|
||||
Err(EncoderError::SessionInitFailed { vendor, status }) => {
|
||||
eprintln!("skip: NVENC session init failed: {vendor} status={status}");
|
||||
return;
|
||||
}
|
||||
Err(other) => {
|
||||
eprintln!("skip: NVENC init unexpected error: {other:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let shared = match try_create_shared_nv12(&device, 1920, 1080) {
|
||||
Some(h) => h,
|
||||
None => {
|
||||
eprintln!("skip: shared NV12 texture creation failed");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let slot = match NvencHandoff::register_slot(&mut handoff, shared, 0, dims) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("skip: register_slot failed: {e:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let pic = PicParams::new(0, true);
|
||||
let submit_result = NvencHandoff::encode_shared_async(&mut handoff, slot, 0, dims, pic);
|
||||
if submit_result.is_err() {
|
||||
eprintln!("skip: encode_shared_async failed: {submit_result:?}");
|
||||
NvencHandoff::unregister_slot(&mut handoff, slot);
|
||||
return;
|
||||
}
|
||||
let mut bitstream = None;
|
||||
for _ in 0..200 {
|
||||
bitstream = NvencHandoff::poll_completed(&mut handoff, slot);
|
||||
if bitstream.is_some() {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
}
|
||||
NvencHandoff::unregister_slot(&mut handoff, slot);
|
||||
let bs = match bitstream {
|
||||
Some(b) => b,
|
||||
None => {
|
||||
eprintln!("nvenc: no bitstream produced within timeout; backend init succeeded");
|
||||
return;
|
||||
}
|
||||
};
|
||||
assert!(!bs.data.is_empty(), "non-empty bitstream");
|
||||
assert!(bs.is_keyframe, "first frame must be keyframe");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn amf_real_encode_one_frame_via_dummy_shared_texture() {
|
||||
let device = match try_create_device_for_vendor(VENDOR_AMD) {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
eprintln!("skip: no AMD D3D11 device available (expected on Intel/NVIDIA-only boxes)");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let dims = EncoderDims::new(1920, 1080);
|
||||
let handoff_result = AmfD3D11Handoff::new(device.clone(), dims, 5_000_000);
|
||||
let mut handoff = match handoff_result {
|
||||
Ok(h) => h,
|
||||
Err(EncoderError::SdkNotFound { .. }) => {
|
||||
eprintln!("skip: AMF runtime DLL not available (expected on non-AMD hardware)");
|
||||
return;
|
||||
}
|
||||
Err(other) => {
|
||||
eprintln!("skip: AMF init unexpected: {other:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let shared = match try_create_shared_nv12(&device, 1920, 1080) {
|
||||
Some(h) => h,
|
||||
None => {
|
||||
eprintln!("skip: shared NV12 texture creation failed");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let slot = match AmfHandoff::register_slot(&mut handoff, shared, 0, dims) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("skip: AMF register_slot failed: {e:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let pic = PicParams::new(0, true);
|
||||
let _ = AmfHandoff::encode_shared_async(&mut handoff, slot, 0, dims, pic);
|
||||
let mut bitstream = None;
|
||||
for _ in 0..200 {
|
||||
bitstream = AmfHandoff::poll_completed(&mut handoff, slot);
|
||||
if bitstream.is_some() {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
}
|
||||
AmfHandoff::unregister_slot(&mut handoff, slot);
|
||||
if let Some(bs) = bitstream {
|
||||
assert!(!bs.data.is_empty(), "non-empty AMF bitstream");
|
||||
} else {
|
||||
eprintln!("amf: no bitstream produced within timeout; backend init succeeded");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn qsv_modern_dispatcher_session_init_succeeds_on_real_iris_xe() {
|
||||
let device = match try_create_device_for_vendor(VENDOR_INTEL) {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
eprintln!("skip: no Intel D3D11 device available");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let dims = EncoderDims::new(1280, 720);
|
||||
let handoff_result = QsvD3D11Handoff::new(device, dims, 5_000_000);
|
||||
match handoff_result {
|
||||
Ok(_) => {
|
||||
eprintln!("qsv: modern dispatcher session + SetHandle + encode_init succeeded");
|
||||
}
|
||||
Err(EncoderError::SdkNotFound { .. }) => {
|
||||
eprintln!("skip: QSV runtime DLL not available");
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("qsv modern dispatcher init MUST succeed on Tiger Lake Iris Xe: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn qsv_real_encode_one_frame_via_dummy_shared_texture() {
|
||||
let device = match try_create_device_for_vendor(VENDOR_INTEL) {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
eprintln!("skip: no Intel D3D11 device available");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let dims = EncoderDims::new(1920, 1080);
|
||||
let handoff_result = QsvD3D11Handoff::new(device.clone(), dims, 5_000_000);
|
||||
let mut handoff = match handoff_result {
|
||||
Ok(h) => h,
|
||||
Err(EncoderError::SdkNotFound { .. }) => {
|
||||
eprintln!("skip: QSV runtime DLL not available");
|
||||
return;
|
||||
}
|
||||
Err(EncoderError::SessionInitFailed { vendor, status }) => {
|
||||
eprintln!("skip: QSV session init failed: {vendor} status={status}");
|
||||
return;
|
||||
}
|
||||
Err(other) => {
|
||||
eprintln!("skip: QSV init unexpected: {other:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let shared = match try_create_shared_nv12(&device, 1920, 1080) {
|
||||
Some(h) => h,
|
||||
None => {
|
||||
eprintln!("skip: shared NV12 texture creation failed");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let slot = match QsvHandoff::register_slot(&mut handoff, shared, 0, dims) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("skip: QSV register_slot failed: {e:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let pic = PicParams::new(0, true);
|
||||
let _ = QsvHandoff::encode_shared_async(&mut handoff, slot, 0, dims, pic);
|
||||
let mut bitstream = None;
|
||||
for _ in 0..200 {
|
||||
bitstream = QsvHandoff::poll_completed(&mut handoff, slot);
|
||||
if bitstream.is_some() {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
}
|
||||
QsvHandoff::unregister_slot(&mut handoff, slot);
|
||||
if let Some(bs) = bitstream {
|
||||
assert!(!bs.data.is_empty(), "non-empty QSV bitstream");
|
||||
} else {
|
||||
eprintln!("qsv: no bitstream produced within timeout; backend init succeeded");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skip_dont_block_rapid_submit_then_poll_never_hangs() {
|
||||
let device = match try_create_device() {
|
||||
Some(d) => d,
|
||||
None => return,
|
||||
};
|
||||
let dims = EncoderDims::new(640, 360);
|
||||
let mut handoff = match NvencD3D11Handoff::new(device.clone(), dims, 1_000_000) {
|
||||
Ok(h) => h,
|
||||
Err(_) => return,
|
||||
};
|
||||
let shared = match try_create_shared_nv12(&device, 640, 360) {
|
||||
Some(h) => h,
|
||||
None => return,
|
||||
};
|
||||
let slot = match NvencHandoff::register_slot(&mut handoff, shared, 0, dims) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return,
|
||||
};
|
||||
let start = std::time::Instant::now();
|
||||
for i in 0..10 {
|
||||
let pic = PicParams::new((i as u64) * 16_666, false);
|
||||
let _ = NvencHandoff::encode_shared_async(&mut handoff, slot, 0, dims, pic);
|
||||
let _ = NvencHandoff::poll_completed(&mut handoff, slot);
|
||||
}
|
||||
let elapsed = start.elapsed();
|
||||
assert!(
|
||||
elapsed.as_secs() < 5,
|
||||
"10 submit+poll cycles must complete in < 5s, was {elapsed:?}"
|
||||
);
|
||||
NvencHandoff::unregister_slot(&mut handoff, slot);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cleanup_on_drop_releases_resources() {
|
||||
let device = match try_create_device() {
|
||||
Some(d) => d,
|
||||
None => return,
|
||||
};
|
||||
let dims = EncoderDims::new(640, 360);
|
||||
{
|
||||
let _handoff = match NvencD3D11Handoff::new(device.clone(), dims, 1_000_000) {
|
||||
Ok(h) => h,
|
||||
Err(_) => return,
|
||||
};
|
||||
}
|
||||
let _handoff2 = NvencD3D11Handoff::new(device.clone(), dims, 1_000_000);
|
||||
}
|
||||
|
||||
struct CountingCallback {
|
||||
seen: Vec<(u64, u32)>,
|
||||
}
|
||||
|
||||
impl EncoderCompletionCallback for CountingCallback {
|
||||
fn on_complete(&mut self, sequence: u64, encoded_bytes: u32) {
|
||||
self.seen.push((sequence, encoded_bytes));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nvenc_compression_handoff_encodes_one_frame_via_keyed_mutex() {
|
||||
let device = match try_create_device_for_vendor(VENDOR_NVIDIA) {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
eprintln!("skip: no NVIDIA D3D11 device available");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let dims = EncoderDims::new(1280, 720);
|
||||
let mut handoff = match NvencD3D11Handoff::new(device.clone(), dims, 2_000_000) {
|
||||
Ok(h) => h,
|
||||
Err(EncoderError::SdkNotFound { .. }) => {
|
||||
eprintln!("skip: NVENC runtime DLL not available");
|
||||
return;
|
||||
}
|
||||
Err(other) => {
|
||||
eprintln!("skip: NVENC init unexpected: {other:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let shared = match try_create_shared_nv12(&device, 1280, 720) {
|
||||
Some(h) => h,
|
||||
None => {
|
||||
eprintln!("skip: shared NV12 texture creation failed");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let submission = EncoderSubmission::new(shared, 0, dims, 1);
|
||||
let mut callback = CountingCallback { seen: Vec::new() };
|
||||
NvencHandoff::encode_shared(&mut handoff, submission, &mut callback)
|
||||
.expect("encode_shared first frame ok");
|
||||
assert_eq!(callback.seen.len(), 1, "callback fired once");
|
||||
assert_eq!(callback.seen[0].0, 1, "callback sequence matches");
|
||||
let mut payload: Option<EncodedBitstream> = None;
|
||||
for _ in 0..200 {
|
||||
let slot = fluxer_encoder_ring::HandoffSlot::new(0, shared);
|
||||
payload = NvencHandoff::poll_completed(&mut handoff, slot);
|
||||
if payload.is_some() {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
}
|
||||
let bs = match payload {
|
||||
Some(b) => b,
|
||||
None => {
|
||||
eprintln!("nvenc keyed-mutex: no bitstream within timeout; init succeeded");
|
||||
return;
|
||||
}
|
||||
};
|
||||
assert!(!bs.data.is_empty(), "keyed-mutex encoded payload non-empty");
|
||||
assert!(bs.is_keyframe, "first frame must be IDR keyframe");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nvenc_compression_handoff_handles_back_pressure() {
|
||||
let device = match try_create_device_for_vendor(VENDOR_NVIDIA) {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
eprintln!("skip: no NVIDIA D3D11 device available");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let dims = EncoderDims::new(640, 360);
|
||||
let mut handoff = match NvencD3D11Handoff::new(device.clone(), dims, 1_000_000) {
|
||||
Ok(h) => h,
|
||||
Err(EncoderError::SdkNotFound { .. }) => {
|
||||
eprintln!("skip: NVENC runtime DLL not available");
|
||||
return;
|
||||
}
|
||||
Err(other) => {
|
||||
eprintln!("skip: NVENC init unexpected: {other:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let shared = match try_create_shared_nv12(&device, 640, 360) {
|
||||
Some(h) => h,
|
||||
None => {
|
||||
eprintln!("skip: shared NV12 texture creation failed");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let total: u64 = (NVENC_COMPLETION_RING_CAPACITY as u64) + 8;
|
||||
let mut callback = CountingCallback { seen: Vec::new() };
|
||||
let mut accepted = 0usize;
|
||||
let mut full_drops = 0usize;
|
||||
for seq in 1..=total {
|
||||
let submission = EncoderSubmission::new(shared, 0, dims, seq);
|
||||
match NvencHandoff::encode_shared(&mut handoff, submission, &mut callback) {
|
||||
Ok(()) => {
|
||||
accepted += 1;
|
||||
}
|
||||
Err(RingError::FullDropped { .. }) => {
|
||||
full_drops += 1;
|
||||
assert_eq!(
|
||||
handoff.pending_completion(),
|
||||
NVENC_COMPLETION_RING_CAPACITY,
|
||||
"pre-encode drop only when completion ring is full"
|
||||
);
|
||||
}
|
||||
Err(other) => panic!("unexpected encode_shared error: {other:?}"),
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(2));
|
||||
}
|
||||
assert_eq!(
|
||||
accepted + full_drops,
|
||||
total as usize,
|
||||
"every submission accounted"
|
||||
);
|
||||
let pending = handoff.pending_completion();
|
||||
assert!(
|
||||
pending <= NVENC_COMPLETION_RING_CAPACITY,
|
||||
"completion ring stays bounded"
|
||||
);
|
||||
let mut drained = 0usize;
|
||||
for _ in 0..400 {
|
||||
let slot = fluxer_encoder_ring::HandoffSlot::new(0, shared);
|
||||
let bs = NvencHandoff::poll_completed(&mut handoff, slot);
|
||||
match bs {
|
||||
Some(_) => {
|
||||
drained += 1;
|
||||
}
|
||||
None => {
|
||||
if drained >= accepted {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(5));
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
drained, accepted,
|
||||
"no accepted frame is silently discarded post-encode"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nvenc_attach_then_detach_smoke() {
|
||||
let device = match try_create_device_for_vendor(VENDOR_NVIDIA) {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
eprintln!("skip: no NVIDIA D3D11 device available");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let dims = EncoderDims::new(640, 360);
|
||||
let handoff_result = NvencD3D11Handoff::new(device.clone(), dims, 1_000_000);
|
||||
let handoff = match handoff_result {
|
||||
Ok(h) => h,
|
||||
Err(EncoderError::SdkNotFound { .. }) => {
|
||||
eprintln!("skip: NVENC runtime DLL not available");
|
||||
return;
|
||||
}
|
||||
Err(other) => {
|
||||
eprintln!("skip: NVENC init unexpected: {other:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
assert_eq!(
|
||||
handoff.pending_completion(),
|
||||
0,
|
||||
"fresh handoff has empty ring"
|
||||
);
|
||||
assert_eq!(handoff.completed_count(), 0, "fresh handoff completed=0");
|
||||
drop(handoff);
|
||||
let _again = NvencD3D11Handoff::new(device, dims, 1_000_000);
|
||||
}
|
||||
@@ -289,34 +289,6 @@ mod tests {
|
||||
assert!(!is_routable_media_class(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn daemon_unreachable_returns_none_from_open() {
|
||||
let bridge = PipeWireBridge::open();
|
||||
if let Some(b) = bridge {
|
||||
let _ = b.inventory();
|
||||
b.release();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn direct_open_returns_none_or_cleans_up() {
|
||||
let direct = PipeWireDirectCapture::open();
|
||||
if let Some(d) = direct {
|
||||
d.stop();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn integration_smoke_apply_release_cycle_is_safe() {
|
||||
let Some(bridge) = PipeWireBridge::open() else {
|
||||
return;
|
||||
};
|
||||
bridge.apply(RoutingRule::default());
|
||||
std::thread::sleep(Duration::from_millis(20));
|
||||
bridge.release();
|
||||
let _ = bridge.inventory();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn virtual_sink_props_match_legacy_contract() {
|
||||
let props = build_virtual_sink_props();
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {execFileSync} from 'node:child_process';
|
||||
import {copyFileSync} from 'node:fs';
|
||||
import {createRequire} from 'node:module';
|
||||
import {setTimeout as sleep} from 'node:timers/promises';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const work = process.env.FLX_WORK;
|
||||
const addonNode = `${work}/flx_direct_addon.node`;
|
||||
copyFileSync(process.env.FLX_ADDON_SO, addonNode);
|
||||
const addon = require(addonNode);
|
||||
|
||||
const results = [];
|
||||
function record(name, ok, detail) {
|
||||
results.push({name, ok});
|
||||
console.log(`${ok ? 'PASS' : 'FAIL'} ${name}${detail ? ` -- ${detail}` : ''}`);
|
||||
}
|
||||
function pwDump() {
|
||||
return JSON.parse(execFileSync('pw-dump', {encoding: 'utf8', maxBuffer: 64e6}));
|
||||
}
|
||||
function directSinkNode(dump) {
|
||||
return dump.find(
|
||||
(o) => o.type === 'PipeWire:Interface:Node' && /^fluxer-direct-capture-/.test(o.info?.props?.['node.name'] || ''),
|
||||
);
|
||||
}
|
||||
async function waitFor(p, ms, step = 150) {
|
||||
const end = Date.now() + ms;
|
||||
let last;
|
||||
while (Date.now() < end) {
|
||||
last = p();
|
||||
if (last) return last;
|
||||
await sleep(step);
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
function rms(samples) {
|
||||
if (samples.length === 0) return 0;
|
||||
let sum = 0;
|
||||
for (const s of samples) sum += s * s;
|
||||
return Math.sqrt(sum / samples.length);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const dc = new addon.DirectAudioCapture();
|
||||
let lifecycle = null;
|
||||
dc.setLifecycleCallback((...args) => {
|
||||
const flat = args.length === 1 && Array.isArray(args[0]) ? args[0] : args;
|
||||
lifecycle = {kind: flat[0], msg: flat[1]};
|
||||
});
|
||||
|
||||
const started = dc.start({include: [{'application.name': 'Music Player Demo'}]});
|
||||
record('DirectAudioCapture.start(include rule) accepted', started === true);
|
||||
|
||||
const sinkUp = await waitFor(() => {
|
||||
const d = pwDump();
|
||||
return directSinkNode(d) ? d : null;
|
||||
}, 8000);
|
||||
record(
|
||||
'hidden private sink fluxer-direct-capture-* created',
|
||||
!!sinkUp,
|
||||
sinkUp ? directSinkNode(sinkUp).info.props['node.name'] : 'timeout',
|
||||
);
|
||||
|
||||
if (sinkUp) {
|
||||
const sinkProps = directSinkNode(sinkUp).info.props;
|
||||
record(
|
||||
'private sink node.hidden=true (not user-visible)',
|
||||
String(sinkProps['node.hidden']) === 'true',
|
||||
`node.hidden=${sinkProps['node.hidden']}`,
|
||||
);
|
||||
record('private sink media.class=Audio/Sink', sinkProps['media.class'] === 'Audio/Sink', sinkProps['media.class']);
|
||||
}
|
||||
|
||||
let maxRms = 0;
|
||||
let frames = 0;
|
||||
let totalSamples = 0;
|
||||
const captureDeadline = Date.now() + 6000;
|
||||
while (Date.now() < captureDeadline) {
|
||||
const f = dc.read();
|
||||
if (f) {
|
||||
const samples = new Float32Array(f.samples);
|
||||
frames += 1;
|
||||
totalSamples += samples.length;
|
||||
maxRms = Math.max(maxRms, rms(samples));
|
||||
if (maxRms > 0.01 && frames > 5) break;
|
||||
}
|
||||
await sleep(20);
|
||||
}
|
||||
record('DirectAudioCapture yields real frames', frames > 0, `${frames} frames, ${totalSamples} samples`);
|
||||
record('captured audio is non-silent (real 440Hz tone tapped)', maxRms > 0.01, `peak rms=${maxRms.toFixed(4)}`);
|
||||
|
||||
const dump = pwDump();
|
||||
const sink = directSinkNode(dump);
|
||||
const sinkId = Number(sink?.id);
|
||||
const linkSrcNodes = new Set(
|
||||
dump
|
||||
.filter((o) => o.type === 'PipeWire:Interface:Link' && Number(o.info?.props?.['link.input.node']) === sinkId)
|
||||
.map((o) => Number(o.info?.props?.['link.output.node'])),
|
||||
);
|
||||
const fluxerStreamIds = dump
|
||||
.filter((o) => o.type === 'PipeWire:Interface:Node' && o.info?.props?.['application.name'] === 'Fluxer')
|
||||
.map((o) => Number(o.id));
|
||||
record(
|
||||
'Fluxer-named app excluded from per-process capture',
|
||||
fluxerStreamIds.every((id) => !linkSrcNodes.has(id)),
|
||||
`fluxer=${fluxerStreamIds} linkedSrc=${[...linkSrcNodes]}`,
|
||||
);
|
||||
|
||||
const musicIds = dump
|
||||
.filter((o) => o.type === 'PipeWire:Interface:Node' && o.info?.props?.['application.name'] === 'Music Player Demo')
|
||||
.map((o) => Number(o.id));
|
||||
record(
|
||||
'targeted app IS linked to the private sink',
|
||||
musicIds.some((id) => linkSrcNodes.has(id)),
|
||||
`music=${musicIds} linkedSrc=${[...linkSrcNodes]}`,
|
||||
);
|
||||
|
||||
dc.stop();
|
||||
await sleep(600);
|
||||
record('stop() emits closed-clean lifecycle', lifecycle?.kind === 'closed-clean', JSON.stringify(lifecycle));
|
||||
const afterStop = pwDump();
|
||||
const sinkAfter = directSinkNode(afterStop);
|
||||
const residualLinks = sinkAfter
|
||||
? afterStop.filter(
|
||||
(o) =>
|
||||
o.type === 'PipeWire:Interface:Link' && Number(o.info?.props?.['link.input.node']) === Number(sinkAfter.id),
|
||||
).length
|
||||
: 0;
|
||||
record('stop() removes capture links', residualLinks === 0, `residual=${residualLinks}`);
|
||||
|
||||
const failed = results.filter((r) => !r.ok).length;
|
||||
console.log(`\n=== direct: ${results.length - failed}/${results.length} checks passed ===`);
|
||||
process.exit(failed === 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error('HARNESS ERROR:', e?.stack || e);
|
||||
process.exit(2);
|
||||
});
|
||||
@@ -1,233 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {execFileSync, spawn} from 'node:child_process';
|
||||
import {copyFileSync} from 'node:fs';
|
||||
import {createRequire} from 'node:module';
|
||||
import {setTimeout as sleep} from 'node:timers/promises';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
const addonSo = req('FLX_ADDON_SO');
|
||||
const work = req('FLX_WORK');
|
||||
const tone = req('FLX_TONE');
|
||||
const addonNode = `${work}/flx_audio_addon.node`;
|
||||
copyFileSync(addonSo, addonNode);
|
||||
const addon = require(addonNode);
|
||||
|
||||
const SINK_NAME = 'fluxer-screen-share';
|
||||
const SINK_DESC = 'Fluxer Screen Share Audio';
|
||||
const results = [];
|
||||
const children = [];
|
||||
|
||||
function req(name) {
|
||||
const v = process.env[name];
|
||||
if (!v) {
|
||||
console.error(`HARNESS ERROR: ${name} not set`);
|
||||
process.exit(2);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
function record(name, ok, detail) {
|
||||
results.push({name, ok});
|
||||
console.log(`${ok ? 'PASS' : 'FAIL'} ${name}${detail ? ` -- ${detail}` : ''}`);
|
||||
}
|
||||
function pwDump() {
|
||||
return JSON.parse(execFileSync('pw-dump', {encoding: 'utf8', maxBuffer: 64e6}));
|
||||
}
|
||||
function nodesByName(dump, name) {
|
||||
return dump.filter((o) => o.type === 'PipeWire:Interface:Node' && o.info?.props?.['node.name'] === name);
|
||||
}
|
||||
function links(dump) {
|
||||
return dump
|
||||
.filter((o) => o.type === 'PipeWire:Interface:Link')
|
||||
.map((o) => ({
|
||||
inNode: Number(o.info?.props?.['link.input.node']),
|
||||
outNode: Number(o.info?.props?.['link.output.node']),
|
||||
}));
|
||||
}
|
||||
function streamNodes(dump) {
|
||||
return dump.filter(
|
||||
(o) => o.type === 'PipeWire:Interface:Node' && o.info?.props?.['media.class'] === 'Stream/Output/Audio',
|
||||
);
|
||||
}
|
||||
async function waitFor(predicate, timeoutMs, stepMs = 150) {
|
||||
const deadline = Date.now() + timeoutMs;
|
||||
let last;
|
||||
while (Date.now() < deadline) {
|
||||
last = predicate();
|
||||
if (last) return last;
|
||||
await sleep(stepMs);
|
||||
}
|
||||
return last;
|
||||
}
|
||||
function killAll() {
|
||||
for (const c of children) {
|
||||
try {
|
||||
c.kill('SIGKILL');
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
record('backend is pipewire', addon.audioBackend() === 'pipewire', addon.audioBackend());
|
||||
|
||||
const descendant = spawn(
|
||||
'pw-play',
|
||||
['--target', 'test_speakers', '-P', '{ application.name = "Descendant Player" }', tone],
|
||||
{
|
||||
stdio: 'ignore',
|
||||
env: process.env,
|
||||
},
|
||||
);
|
||||
children.push(descendant);
|
||||
|
||||
const speakerIdOf = (d) => nodesByName(d, 'test_speakers')[0]?.id;
|
||||
const sourcesInto = (dump) => {
|
||||
const sid = Number(speakerIdOf(dump));
|
||||
return new Set(
|
||||
links(dump)
|
||||
.filter((l) => l.inNode === sid)
|
||||
.map((l) => l.outNode),
|
||||
).size;
|
||||
};
|
||||
|
||||
const pre = await waitFor(() => {
|
||||
const d = pwDump();
|
||||
return streamNodes(d).length >= 5 && sourcesInto(d) >= 5 ? d : null;
|
||||
}, 12000);
|
||||
record(
|
||||
'all 5 playback streams routed to speakers pre-capture',
|
||||
!!pre,
|
||||
pre ? `${streamNodes(pre).length} streams, ${sourcesInto(pre)} routed` : 'timeout',
|
||||
);
|
||||
const preDump = pre || pwDump();
|
||||
|
||||
const speakers = nodesByName(preDump, 'test_speakers');
|
||||
record('default sink test_speakers exists', speakers.length === 1);
|
||||
const preSpeakerSources = sourcesInto(preDump);
|
||||
record(
|
||||
'every app is playing to the real speakers pre-capture',
|
||||
preSpeakerSources >= 5,
|
||||
`${preSpeakerSources} distinct app streams -> speakers`,
|
||||
);
|
||||
|
||||
const bridge = new addon.AudioBridge();
|
||||
record('AudioBridge on pipewire', bridge.backend() === 'pipewire', bridge.backend());
|
||||
record(
|
||||
'apply(system rule) accepted',
|
||||
bridge.apply({onlySpeakers: true, onlyDefaultSpeakers: true, ignoreDevices: true}) === true,
|
||||
);
|
||||
|
||||
const after = await waitFor(() => {
|
||||
const d = pwDump();
|
||||
if (nodesByName(d, SINK_NAME).length !== 1) return null;
|
||||
const g = bridge.routingGraph();
|
||||
return g.ownedLinks.length >= 2 ? {d, g} : null;
|
||||
}, 10000);
|
||||
|
||||
if (!after) {
|
||||
record('fluxer sink + capture links established', false, 'timeout');
|
||||
await finish(bridge);
|
||||
return;
|
||||
}
|
||||
const {d: dump, g: graph} = after;
|
||||
|
||||
const sink = nodesByName(dump, SINK_NAME);
|
||||
record('exactly one fluxer-screen-share node', sink.length === 1, `count=${sink.length}`);
|
||||
const sp = sink[0]?.info?.props ?? {};
|
||||
record(
|
||||
'sink node.description is "Fluxer Screen Share Audio"',
|
||||
sp['node.description'] === SINK_DESC,
|
||||
sp['node.description'],
|
||||
);
|
||||
record('sink media.class is Audio/Source/Virtual', sp['media.class'] === 'Audio/Source/Virtual', sp['media.class']);
|
||||
record('sink node.virtual=true', String(sp['node.virtual']) === 'true');
|
||||
const sinkId = Number(sink[0]?.id);
|
||||
|
||||
record(
|
||||
'all owned links are passive',
|
||||
graph.ownedLinks.every((l) => l.passive === true),
|
||||
`${graph.ownedLinks.length} links`,
|
||||
);
|
||||
record(
|
||||
'all owned links terminate at the fluxer sink',
|
||||
graph.ownedLinks.every((l) => Number(l.inputNodeId) === sinkId),
|
||||
);
|
||||
|
||||
const captured = new Set(graph.ownedLinks.map((l) => Number(l.outputNodeId)));
|
||||
const idsByPredicate = (pred) =>
|
||||
streamNodes(dump)
|
||||
.filter((o) => pred(o.info.props))
|
||||
.map((o) => Number(o.id));
|
||||
|
||||
const normalIds = idsByPredicate(
|
||||
(p) =>
|
||||
['pw-play', 'Music Player Demo'].includes(p['application.name']) &&
|
||||
p['application.name'] !== 'Fluxer' &&
|
||||
p['application.name'] !== 'Descendant Player' &&
|
||||
!(p['node.name'] || '').startsWith('Fluxer '),
|
||||
);
|
||||
const fluxerAppIds = idsByPredicate((p) => p['application.name'] === 'Fluxer');
|
||||
const fluxerNodeIds = idsByPredicate((p) => (p['node.name'] || '').startsWith('Fluxer '));
|
||||
const descendantIds = idsByPredicate((p) => p['application.name'] === 'Descendant Player');
|
||||
|
||||
record(
|
||||
'normal external apps ARE captured',
|
||||
normalIds.length >= 2 && normalIds.every((id) => captured.has(id)),
|
||||
`normal=${normalIds} captured=${[...captured]}`,
|
||||
);
|
||||
record(
|
||||
'Fluxer-named app (application.name) is EXCLUDED',
|
||||
fluxerAppIds.length >= 1 && fluxerAppIds.every((id) => !captured.has(id)),
|
||||
`fluxerApp=${fluxerAppIds}`,
|
||||
);
|
||||
record(
|
||||
'Fluxer-named app (node.name prefix) is EXCLUDED',
|
||||
fluxerNodeIds.length >= 1 && fluxerNodeIds.every((id) => !captured.has(id)),
|
||||
`fluxerNode=${fluxerNodeIds}`,
|
||||
);
|
||||
record(
|
||||
'descendant-PID player is EXCLUDED (self-process tree)',
|
||||
descendantIds.length >= 1 && descendantIds.every((id) => !captured.has(id)),
|
||||
`descendant=${descendantIds}`,
|
||||
);
|
||||
|
||||
const afterSpeakerSources = sourcesInto(dump);
|
||||
record(
|
||||
'apps STILL play to real speakers during capture (tap, not move)',
|
||||
afterSpeakerSources >= preSpeakerSources,
|
||||
`before=${preSpeakerSources} after=${afterSpeakerSources} distinct app streams -> speakers`,
|
||||
);
|
||||
|
||||
const meta = dump.find((o) => o.type === 'PipeWire:Interface:Metadata' && o.props?.['metadata.name'] === 'default');
|
||||
const def = meta?.metadata?.find((m) => m.key === 'default.audio.sink')?.value?.name;
|
||||
record('default audio sink unchanged (test_speakers)', def === 'test_speakers', `default=${def}`);
|
||||
|
||||
await finish(bridge);
|
||||
}
|
||||
|
||||
async function finish(bridge) {
|
||||
bridge.release();
|
||||
const cleared = await waitFor(() => (bridge.routingGraph().ownedLinks.length === 0 ? true : null), 5000);
|
||||
record(
|
||||
'release()+settle removes all owned links',
|
||||
!!cleared,
|
||||
cleared ? '0 owned links' : `still ${bridge.routingGraph().ownedLinks.length}`,
|
||||
);
|
||||
await sleep(500);
|
||||
const d = pwDump();
|
||||
const sinkId = Number(nodesByName(d, SINK_NAME)[0]?.id);
|
||||
const residual = Number.isNaN(sinkId) ? 0 : links(d).filter((l) => l.inNode === sinkId).length;
|
||||
record('no residual links into fluxer sink after release', residual === 0, `residual=${residual}`);
|
||||
killAll();
|
||||
const failed = results.filter((r) => !r.ok).length;
|
||||
console.log(`\n=== ${results.length - failed}/${results.length} checks passed ===`);
|
||||
process.exit(failed === 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
process.on('exit', killAll);
|
||||
main().catch((e) => {
|
||||
killAll();
|
||||
console.error('HARNESS ERROR:', e?.stack || e);
|
||||
process.exit(2);
|
||||
});
|
||||
@@ -1,78 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
WORK="${FLX_WORK:-/home/parallels/flx-vmtest}"
|
||||
ADDON_SO="${FLX_ADDON_SO:-/home/parallels/flx-target/debug/libfluxer_linux_audio_capture.so}"
|
||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
mkdir -p "$WORK"
|
||||
export XDG_RUNTIME_DIR="$WORK/xdg"
|
||||
mkdir -p "$XDG_RUNTIME_DIR"; chmod 700 "$XDG_RUNTIME_DIR"
|
||||
export PIPEWIRE_RUNTIME_DIR="$XDG_RUNTIME_DIR"
|
||||
export PULSE_RUNTIME_PATH="$XDG_RUNTIME_DIR/pulse"
|
||||
unset DISPLAY WAYLAND_DISPLAY DBUS_SESSION_BUS_ADDRESS
|
||||
|
||||
PIDS=()
|
||||
cleanup() {
|
||||
for p in "${PIDS[@]:-}"; do kill -9 "$p" 2>/dev/null; done
|
||||
pkill -9 -u "$(id -un)" -f "pipewire" 2>/dev/null
|
||||
pkill -9 -u "$(id -un)" -f "wireplumber" 2>/dev/null
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
echo "=== starting private pipewire server (runtime=$XDG_RUNTIME_DIR) ==="
|
||||
pipewire >"$WORK/pipewire.log" 2>&1 & PIDS+=($!)
|
||||
sleep 0.8
|
||||
pipewire-pulse >"$WORK/pipewire-pulse.log" 2>&1 & PIDS+=($!)
|
||||
sleep 0.5
|
||||
wireplumber >"$WORK/wireplumber.log" 2>&1 & PIDS+=($!)
|
||||
|
||||
for _ in $(seq 1 40); do pw-cli info 0 >/dev/null 2>&1 && break; sleep 0.25; done
|
||||
if ! pw-cli info 0 >/dev/null 2>&1; then
|
||||
echo "FATAL: private pipewire did not come up"; cat "$WORK/pipewire.log"; exit 2
|
||||
fi
|
||||
|
||||
echo "=== creating virtual speakers (default sink) ==="
|
||||
pactl load-module module-null-sink sink_name=test_speakers sink_properties='device.description=Test_Speakers' >/dev/null 2>&1
|
||||
pactl set-default-sink test_speakers 2>/dev/null
|
||||
sleep 0.4
|
||||
|
||||
echo "=== generating a real 600s stereo tone ==="
|
||||
TONE="$WORK/tone.wav"
|
||||
[ -f "$TONE" ] || ffmpeg -hide_banner -loglevel error -f lavfi -i "sine=frequency=440:duration=600" -ac 2 -ar 48000 "$TONE" </dev/null
|
||||
export FLX_TONE="$TONE"
|
||||
|
||||
echo "=== spawning 4 external real-app players (outside the harness process tree) ==="
|
||||
pw-play --target test_speakers "$TONE" >/dev/null 2>&1 & PIDS+=($!)
|
||||
pw-play --target test_speakers -P '{ application.name = "Music Player Demo" }' "$TONE" >/dev/null 2>&1 & PIDS+=($!)
|
||||
pw-play --target test_speakers -P '{ application.name = "Fluxer" }' "$TONE" >/dev/null 2>&1 & PIDS+=($!)
|
||||
pw-play --target test_speakers -P '{ node.name = "Fluxer Helper Stream" }' "$TONE" >/dev/null 2>&1 & PIDS+=($!)
|
||||
sleep 1.5
|
||||
|
||||
echo "=== pre-test graph (fluxer sink should be ABSENT) ==="
|
||||
pw-dump | node -e 'const d=JSON.parse(require("fs").readFileSync(0));const f=d.filter(o=>o.type==="PipeWire:Interface:Node"&&/fluxer-screen-share/.test(o.info?.props?.["node.name"]||""));console.log("fluxer sink nodes pre-test:",f.length);'
|
||||
|
||||
echo "=== running napi SYSTEM-capture validation harness ==="
|
||||
export FLX_ADDON_SO="$ADDON_SO" FLX_WORK="$WORK"
|
||||
node "$HERE/pw_graph_validation.mjs"
|
||||
HARNESS_RC=$?
|
||||
|
||||
echo "=== running napi DIRECT (per-process) capture validation harness ==="
|
||||
node "$HERE/direct_capture_validation.mjs"
|
||||
DIRECT_RC=$?
|
||||
[ "$DIRECT_RC" = "0" ] || HARNESS_RC=$DIRECT_RC
|
||||
|
||||
echo "=== post-exit cleanup check (Drop must remove the fluxer sink) ==="
|
||||
sleep 0.8
|
||||
RESIDUAL=$(pw-dump | node -e 'const d=JSON.parse(require("fs").readFileSync(0));const f=d.filter(o=>(o.type==="PipeWire:Interface:Node"&&/fluxer/.test(o.info?.props?.["node.name"]||""))||(o.type==="PipeWire:Interface:Link"&&/fluxer/.test(JSON.stringify(o.info?.props||{}))));console.log(f.length);')
|
||||
if [ "$RESIDUAL" = "0" ]; then
|
||||
echo "PASS no fluxer nodes/links remain after addon process exit (clean teardown)"
|
||||
else
|
||||
echo "FAIL $RESIDUAL residual fluxer objects after addon process exit"
|
||||
HARNESS_RC=1
|
||||
fi
|
||||
|
||||
echo "=== DONE rc=$HARNESS_RC ==="
|
||||
exit $HARNESS_RC
|
||||
@@ -525,671 +525,3 @@ pub fn try_acquire_device() -> Option<(wgpu::Device, wgpu::Queue, wgpu::Instance
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use fluxer_gpu_rebuild::{GpuLossRegistry, RebuildOutcome};
|
||||
|
||||
struct GpuCtx {
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
_instance: wgpu::Instance,
|
||||
}
|
||||
|
||||
fn gpu_ctx() -> Option<GpuCtx> {
|
||||
let (device, queue, instance) = try_acquire_device()?;
|
||||
Some(GpuCtx {
|
||||
device,
|
||||
queue,
|
||||
_instance: instance,
|
||||
})
|
||||
}
|
||||
|
||||
fn make_source_texture(
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
width: u32,
|
||||
height: u32,
|
||||
pixels: &[u8],
|
||||
) -> wgpu::Texture {
|
||||
assert_eq!(
|
||||
pixels.len() as u64,
|
||||
u64::from(width) * u64::from(height) * 4
|
||||
);
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("nv12_gpu_pack.test_source"),
|
||||
size: wgpu::Extent3d {
|
||||
width,
|
||||
height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rgba8Unorm,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||
view_formats: &[],
|
||||
});
|
||||
queue.write_texture(
|
||||
wgpu::TexelCopyTextureInfo {
|
||||
texture: &texture,
|
||||
mip_level: 0,
|
||||
origin: wgpu::Origin3d::ZERO,
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
},
|
||||
pixels,
|
||||
wgpu::TexelCopyBufferLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: Some(width * 4),
|
||||
rows_per_image: Some(height),
|
||||
},
|
||||
wgpu::Extent3d {
|
||||
width,
|
||||
height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
);
|
||||
texture
|
||||
}
|
||||
|
||||
fn make_storage_buffer(device: &wgpu::Device, size: u64, label: &str) -> wgpu::Buffer {
|
||||
device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some(label),
|
||||
size,
|
||||
usage: wgpu::BufferUsages::STORAGE
|
||||
| wgpu::BufferUsages::COPY_SRC
|
||||
| wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_buffer(device: &wgpu::Device, queue: &wgpu::Queue, src: &wgpu::Buffer) -> Vec<u8> {
|
||||
let size = src.size();
|
||||
let staging = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("nv12_gpu_pack.test_staging"),
|
||||
size,
|
||||
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("nv12_gpu_pack.test_readback"),
|
||||
});
|
||||
encoder.copy_buffer_to_buffer(src, 0, &staging, 0, size);
|
||||
queue.submit(std::iter::once(encoder.finish()));
|
||||
let slice = staging.slice(..);
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
slice.map_async(wgpu::MapMode::Read, move |res| {
|
||||
let _ = tx.send(res);
|
||||
});
|
||||
let _ = device.poll(wgpu::PollType::Wait {
|
||||
submission_index: None,
|
||||
timeout: None,
|
||||
});
|
||||
let recv = rx.recv();
|
||||
assert!(recv.is_ok(), "channel must deliver map result");
|
||||
let map_result = recv.expect("map result channel");
|
||||
assert!(map_result.is_ok(), "map_async must succeed");
|
||||
let data = {
|
||||
let range = slice.get_mapped_range();
|
||||
range.to_vec()
|
||||
};
|
||||
staging.unmap();
|
||||
data
|
||||
}
|
||||
|
||||
fn pack_and_readback(
|
||||
ctx: &GpuCtx,
|
||||
packer: &Nv12Packer,
|
||||
texture: &wgpu::Texture,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> (Vec<u8>, Vec<u8>) {
|
||||
let y_size = Nv12Packer::y_plane_size(width, height);
|
||||
let uv_size = Nv12Packer::uv_plane_size(width, height);
|
||||
let y_buf = make_storage_buffer(&ctx.device, y_size, "y");
|
||||
let uv_buf = make_storage_buffer(&ctx.device, uv_size, "uv");
|
||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = ctx
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("nv12_gpu_pack.test_encoder"),
|
||||
});
|
||||
let result = packer.pack(PackJob {
|
||||
device: &ctx.device,
|
||||
queue: &ctx.queue,
|
||||
encoder: &mut encoder,
|
||||
source: &view,
|
||||
y_out: &y_buf,
|
||||
uv_out: &uv_buf,
|
||||
dims: (width, height),
|
||||
});
|
||||
assert!(result.is_ok(), "pack must succeed: {result:?}");
|
||||
ctx.queue.submit(std::iter::once(encoder.finish()));
|
||||
let y = read_buffer(&ctx.device, &ctx.queue, &y_buf);
|
||||
let uv = read_buffer(&ctx.device, &ctx.queue, &uv_buf);
|
||||
(y, uv)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn solid_gray_packs_to_expected_bt709_limited() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 16;
|
||||
let height: u32 = 8;
|
||||
let mut pixels = vec![0u8; (width * height * 4) as usize];
|
||||
for chunk in pixels.chunks_exact_mut(4) {
|
||||
chunk[0] = 128;
|
||||
chunk[1] = 128;
|
||||
chunk[2] = 128;
|
||||
chunk[3] = 255;
|
||||
}
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
let (y, uv) = pack_and_readback(&ctx, &packer, &tex, width, height);
|
||||
for value in &y {
|
||||
assert!(
|
||||
(124..=128).contains(value),
|
||||
"Y value {value} out of expected range for gray 128"
|
||||
);
|
||||
}
|
||||
for value in &uv {
|
||||
assert!(
|
||||
(126..=130).contains(value),
|
||||
"UV value {value} out of expected range for neutral gray"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn solid_red_v_is_above_neutral() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 16;
|
||||
let height: u32 = 8;
|
||||
let mut pixels = vec![0u8; (width * height * 4) as usize];
|
||||
for chunk in pixels.chunks_exact_mut(4) {
|
||||
chunk[0] = 255;
|
||||
chunk[1] = 0;
|
||||
chunk[2] = 0;
|
||||
chunk[3] = 255;
|
||||
}
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
let (_y, uv) = pack_and_readback(&ctx, &packer, &tex, width, height);
|
||||
for pair in uv.chunks_exact(2) {
|
||||
let u = pair[0];
|
||||
let v = pair[1];
|
||||
assert!(u < 128, "expected U below neutral for pure red, got {u}");
|
||||
assert!(
|
||||
v > 200,
|
||||
"expected V well above neutral for pure red, got {v}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_dims_exceeding_max() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let max_w: u32 = 8;
|
||||
let max_h: u32 = 8;
|
||||
let packer = Nv12Packer::new(&ctx.device, max_w, max_h);
|
||||
let y_size = Nv12Packer::y_plane_size(16, 16);
|
||||
let uv_size = Nv12Packer::uv_plane_size(16, 16);
|
||||
let y_buf = make_storage_buffer(&ctx.device, y_size, "y");
|
||||
let uv_buf = make_storage_buffer(&ctx.device, uv_size, "uv");
|
||||
let dummy_pixels = vec![0u8; (max_w * max_h * 4) as usize];
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, max_w, max_h, &dummy_pixels);
|
||||
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = ctx
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||
let result = packer.pack(PackJob {
|
||||
device: &ctx.device,
|
||||
queue: &ctx.queue,
|
||||
encoder: &mut encoder,
|
||||
source: &view,
|
||||
y_out: &y_buf,
|
||||
uv_out: &uv_buf,
|
||||
dims: (16, 16),
|
||||
});
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(Nv12PackError::DimensionsExceedMax { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_odd_dims() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let packer = Nv12Packer::new(&ctx.device, 32, 32);
|
||||
let y_buf = make_storage_buffer(&ctx.device, 64, "y");
|
||||
let uv_buf = make_storage_buffer(&ctx.device, 64, "uv");
|
||||
let dummy_pixels = vec![0u8; (8 * 8 * 4) as usize];
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, 8, 8, &dummy_pixels);
|
||||
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = ctx
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||
let result = packer.pack(PackJob {
|
||||
device: &ctx.device,
|
||||
queue: &ctx.queue,
|
||||
encoder: &mut encoder,
|
||||
source: &view,
|
||||
y_out: &y_buf,
|
||||
uv_out: &uv_buf,
|
||||
dims: (7, 8),
|
||||
});
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(Nv12PackError::DimensionsNotEven { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn determinism_two_runs_same_input_same_output() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 32;
|
||||
let height: u32 = 16;
|
||||
let mut pixels = vec![0u8; (width * height * 4) as usize];
|
||||
for (i, chunk) in pixels.chunks_exact_mut(4).enumerate() {
|
||||
let v = (i % 251) as u8;
|
||||
chunk[0] = v;
|
||||
chunk[1] = v.wrapping_mul(2);
|
||||
chunk[2] = v.wrapping_add(31);
|
||||
chunk[3] = 255;
|
||||
}
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
let (y1, uv1) = pack_and_readback(&ctx, &packer, &tex, width, height);
|
||||
let (y2, uv2) = pack_and_readback(&ctx, &packer, &tex, width, height);
|
||||
assert_eq!(y1, y2, "Y plane must be deterministic");
|
||||
assert_eq!(uv1, uv2, "UV plane must be deterministic");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn buffer_too_small_is_rejected() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let packer = Nv12Packer::new(&ctx.device, 64, 64);
|
||||
let y_buf = make_storage_buffer(&ctx.device, 16, "y");
|
||||
let uv_buf = make_storage_buffer(&ctx.device, 16, "uv");
|
||||
let pixels = vec![0u8; (16 * 16 * 4) as usize];
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, 16, 16, &pixels);
|
||||
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = ctx
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||
let result = packer.pack(PackJob {
|
||||
device: &ctx.device,
|
||||
queue: &ctx.queue,
|
||||
encoder: &mut encoder,
|
||||
source: &view,
|
||||
y_out: &y_buf,
|
||||
uv_out: &uv_buf,
|
||||
dims: (16, 16),
|
||||
});
|
||||
assert!(matches!(result, Err(Nv12PackError::YBufferTooSmall { .. })));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packer_is_ready_only_when_built() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let mut packer = Nv12Packer::new(&ctx.device, 32, 32);
|
||||
assert!(
|
||||
packer.is_ready(),
|
||||
"freshly constructed packer must be ready"
|
||||
);
|
||||
packer.release();
|
||||
assert!(!packer.is_ready(), "released packer must not be ready");
|
||||
let rebuilt = packer.rebuild(&ctx.device, &ctx.queue);
|
||||
assert!(rebuilt.is_ok(), "rebuild on a fresh device must succeed");
|
||||
assert!(packer.is_ready(), "rebuilt packer must be ready");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pack_between_release_and_rebuild_returns_not_ready() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let mut packer = Nv12Packer::new(&ctx.device, 16, 16);
|
||||
packer.release();
|
||||
let y_buf = make_storage_buffer(&ctx.device, 256, "y");
|
||||
let uv_buf = make_storage_buffer(&ctx.device, 256, "uv");
|
||||
let dummy_pixels = vec![0u8; (16 * 16 * 4) as usize];
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, 16, 16, &dummy_pixels);
|
||||
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = ctx
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||
let result = packer.pack(PackJob {
|
||||
device: &ctx.device,
|
||||
queue: &ctx.queue,
|
||||
encoder: &mut encoder,
|
||||
source: &view,
|
||||
y_out: &y_buf,
|
||||
uv_out: &uv_buf,
|
||||
dims: (16, 16),
|
||||
});
|
||||
assert!(matches!(result, Err(Nv12PackError::NotReady)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registry_release_then_rebuild_invokes_callback_in_order() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let registry = GpuLossRegistry::new();
|
||||
let packer = Box::new(Nv12Packer::new(&ctx.device, 16, 16));
|
||||
let _guard = registry.register(packer);
|
||||
let report = registry.handle_device_lost(&ctx.device, &ctx.queue);
|
||||
assert_eq!(report.released_count, 1);
|
||||
assert_eq!(report.rebuilt_count, 1);
|
||||
assert_eq!(report.failed_count, 0);
|
||||
assert!(report.is_total_success());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn after_loss_handling_packer_can_pack_again() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 16;
|
||||
let height: u32 = 8;
|
||||
let pixels = vec![128u8; (width * height * 4) as usize];
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let mut packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
packer.release();
|
||||
packer
|
||||
.rebuild(&ctx.device, &ctx.queue)
|
||||
.expect("rebuild must succeed after release");
|
||||
let (y, _uv) = pack_and_readback(&ctx, &packer, &tex, width, height);
|
||||
assert!(!y.is_empty(), "Y plane must be non-empty after rebuild");
|
||||
assert!(
|
||||
(124..=128).contains(&y[0]),
|
||||
"Y value out of range for gray 128 after rebuild: {}",
|
||||
y[0]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn determinism_across_rebuild_round_trip() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 32;
|
||||
let height: u32 = 16;
|
||||
let mut pixels = vec![0u8; (width * height * 4) as usize];
|
||||
for (i, chunk) in pixels.chunks_exact_mut(4).enumerate() {
|
||||
let v = (i % 251) as u8;
|
||||
chunk[0] = v;
|
||||
chunk[1] = v.wrapping_mul(2);
|
||||
chunk[2] = v.wrapping_add(31);
|
||||
chunk[3] = 255;
|
||||
}
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let mut packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
let (y1, uv1) = pack_and_readback(&ctx, &packer, &tex, width, height);
|
||||
packer.release();
|
||||
packer
|
||||
.rebuild(&ctx.device, &ctx.queue)
|
||||
.expect("rebuild must succeed");
|
||||
let (y2, uv2) = pack_and_readback(&ctx, &packer, &tex, width, height);
|
||||
assert_eq!(y1, y2, "Y plane must match across rebuild round-trip");
|
||||
assert_eq!(uv1, uv2, "UV plane must match across rebuild round-trip");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_rebuild_without_release_is_owner_invariant_error() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let mut packer = Nv12Packer::new(&ctx.device, 16, 16);
|
||||
let outcome = packer.rebuild(&ctx.device, &ctx.queue);
|
||||
assert!(matches!(
|
||||
outcome,
|
||||
Err(GpuRebuildError::OwnerInvariantBroken { .. })
|
||||
));
|
||||
}
|
||||
|
||||
struct FailingRebuildOwner {
|
||||
ready: bool,
|
||||
}
|
||||
|
||||
impl FailingRebuildOwner {
|
||||
fn new() -> Self {
|
||||
Self { ready: true }
|
||||
}
|
||||
}
|
||||
|
||||
impl GpuLossCallback for FailingRebuildOwner {
|
||||
fn release(&mut self) {
|
||||
let was_ready = self.ready;
|
||||
assert!(
|
||||
was_ready,
|
||||
"FailingRebuildOwner release must observe ready state",
|
||||
);
|
||||
self.ready = false;
|
||||
assert!(!self.ready, "FailingRebuildOwner postcondition");
|
||||
}
|
||||
fn rebuild(
|
||||
&mut self,
|
||||
_device: &wgpu::Device,
|
||||
_queue: &wgpu::Queue,
|
||||
) -> Result<(), GpuRebuildError> {
|
||||
assert!(
|
||||
!self.ready,
|
||||
"rebuild must run after release in FailingRebuildOwner"
|
||||
);
|
||||
Err(GpuRebuildError::DeviceRejected {
|
||||
reason: "synthetic second-device failure",
|
||||
})
|
||||
}
|
||||
fn is_ready(&self) -> bool {
|
||||
self.ready
|
||||
}
|
||||
fn debug_label(&self) -> &'static str {
|
||||
"test.failing_rebuild_owner"
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_rebuild_surfaces_in_report_and_owner_reports_not_ready() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let registry = GpuLossRegistry::new();
|
||||
let _guard = registry.register(Box::new(FailingRebuildOwner::new()));
|
||||
let report = registry.handle_device_lost(&ctx.device, &ctx.queue);
|
||||
assert_eq!(report.released_count, 1);
|
||||
assert_eq!(report.failed_count, 1);
|
||||
assert_eq!(report.rebuilt_count, 0);
|
||||
assert!(!report.is_total_success());
|
||||
let failed = report
|
||||
.outcomes
|
||||
.iter()
|
||||
.filter(|o| matches!(o, RebuildOutcome::Failed { .. }))
|
||||
.count();
|
||||
assert_eq!(failed, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_packers_release_lifo_rebuild_fifo() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let registry = GpuLossRegistry::new();
|
||||
let mut guards = Vec::new();
|
||||
for _ in 0..3u32 {
|
||||
let packer = Box::new(Nv12Packer::new(&ctx.device, 16, 16));
|
||||
guards.push(registry.register(packer));
|
||||
}
|
||||
assert_eq!(registry.len(), 3);
|
||||
let report = registry.handle_device_lost(&ctx.device, &ctx.queue);
|
||||
assert_eq!(report.released_count, 3);
|
||||
assert_eq!(report.rebuilt_count, 3);
|
||||
assert_eq!(report.failed_count, 0);
|
||||
for outcome in &report.outcomes {
|
||||
assert!(matches!(outcome, RebuildOutcome::Rebuilt { .. }));
|
||||
}
|
||||
drop(guards);
|
||||
}
|
||||
|
||||
fn pack_one_pass(
|
||||
ctx: &GpuCtx,
|
||||
packer: &Nv12Packer,
|
||||
view: &wgpu::TextureView,
|
||||
y_buf: &wgpu::Buffer,
|
||||
uv_buf: &wgpu::Buffer,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) {
|
||||
let mut encoder = ctx
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("nv12_gpu_pack.cache_test_encoder"),
|
||||
});
|
||||
let result = packer.pack(PackJob {
|
||||
device: &ctx.device,
|
||||
queue: &ctx.queue,
|
||||
encoder: &mut encoder,
|
||||
source: view,
|
||||
y_out: y_buf,
|
||||
uv_out: uv_buf,
|
||||
dims: (width, height),
|
||||
});
|
||||
assert!(result.is_ok(), "pack must succeed: {result:?}");
|
||||
ctx.queue.submit(std::iter::once(encoder.finish()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_hit_when_pack_called_twice_with_same_inputs() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 32;
|
||||
let height: u32 = 16;
|
||||
let pixels = vec![64u8; (width * height * 4) as usize];
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
let y_buf = make_storage_buffer(&ctx.device, Nv12Packer::y_plane_size(width, height), "y");
|
||||
let uv_buf =
|
||||
make_storage_buffer(&ctx.device, Nv12Packer::uv_plane_size(width, height), "uv");
|
||||
assert_eq!(packer.cached_bind_group_count(), 0);
|
||||
pack_one_pass(&ctx, &packer, &view, &y_buf, &uv_buf, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
pack_one_pass(&ctx, &packer, &view, &y_buf, &uv_buf, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_miss_when_buffers_change() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 32;
|
||||
let height: u32 = 16;
|
||||
let pixels = vec![64u8; (width * height * 4) as usize];
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
let y_buf_a =
|
||||
make_storage_buffer(&ctx.device, Nv12Packer::y_plane_size(width, height), "y_a");
|
||||
let uv_buf_a = make_storage_buffer(
|
||||
&ctx.device,
|
||||
Nv12Packer::uv_plane_size(width, height),
|
||||
"uv_a",
|
||||
);
|
||||
pack_one_pass(&ctx, &packer, &view, &y_buf_a, &uv_buf_a, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
let y_buf_b =
|
||||
make_storage_buffer(&ctx.device, Nv12Packer::y_plane_size(width, height), "y_b");
|
||||
let uv_buf_b = make_storage_buffer(
|
||||
&ctx.device,
|
||||
Nv12Packer::uv_plane_size(width, height),
|
||||
"uv_b",
|
||||
);
|
||||
pack_one_pass(&ctx, &packer, &view, &y_buf_b, &uv_buf_b, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
pack_one_pass(&ctx, &packer, &view, &y_buf_a, &uv_buf_a, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_invalidated_on_release() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 32;
|
||||
let height: u32 = 16;
|
||||
let pixels = vec![64u8; (width * height * 4) as usize];
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
let y_buf = make_storage_buffer(&ctx.device, Nv12Packer::y_plane_size(width, height), "y");
|
||||
let uv_buf =
|
||||
make_storage_buffer(&ctx.device, Nv12Packer::uv_plane_size(width, height), "uv");
|
||||
pack_one_pass(&ctx, &packer, &view, &y_buf, &uv_buf, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
packer.release();
|
||||
assert_eq!(packer.cached_bind_group_count(), 0);
|
||||
packer
|
||||
.rebuild(&ctx.device, &ctx.queue)
|
||||
.expect("rebuild must succeed");
|
||||
assert_eq!(packer.cached_bind_group_count(), 0);
|
||||
pack_one_pass(&ctx, &packer, &view, &y_buf, &uv_buf, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_replaces_when_source_view_changes() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 32;
|
||||
let height: u32 = 16;
|
||||
let pixels = vec![64u8; (width * height * 4) as usize];
|
||||
let tex_a = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let tex_b = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let view_a = tex_a.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let view_b = tex_b.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
let y_buf = make_storage_buffer(&ctx.device, Nv12Packer::y_plane_size(width, height), "y");
|
||||
let uv_buf =
|
||||
make_storage_buffer(&ctx.device, Nv12Packer::uv_plane_size(width, height), "uv");
|
||||
pack_one_pass(&ctx, &packer, &view_a, &y_buf, &uv_buf, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
pack_one_pass(&ctx, &packer, &view_b, &y_buf, &uv_buf, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
const {existsSync} = require('node:fs');
|
||||
const {join, sep} = require('node:path');
|
||||
const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs');
|
||||
const {nativeFileName} = require('./pure.cjs');
|
||||
const MODULE_NAME = '@fluxer/platform-info';
|
||||
const SKIP_NATIVE_PROBE_ENV = 'FLUXER_PLATFORM_INFO_SKIP_NATIVE_PROBE';
|
||||
|
||||
@@ -13,21 +14,6 @@ function resolveNativeRoot() {
|
||||
return existsSync(unpackedDir) ? unpackedDir : __dirname;
|
||||
}
|
||||
|
||||
function nativeFileName(platform = process.platform, arch = process.arch) {
|
||||
switch (platform) {
|
||||
case 'darwin':
|
||||
if (arch === 'x64' || arch === 'arm64') return `platform-info.darwin-${arch}.node`;
|
||||
break;
|
||||
case 'win32':
|
||||
if (arch === 'x64' || arch === 'arm64') return `platform-info.win32-${arch}-msvc.node`;
|
||||
break;
|
||||
case 'linux':
|
||||
if (arch === 'x64' || arch === 'arm64') return `platform-info.linux-${arch}-gnu.node`;
|
||||
break;
|
||||
}
|
||||
throw new Error(`Unsupported platform-info target: ${platform}/${arch}`);
|
||||
}
|
||||
|
||||
let binding = null;
|
||||
let loadError = null;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"index.js",
|
||||
"index.d.ts",
|
||||
"loader-diagnostics.cjs",
|
||||
"pure.cjs",
|
||||
"platform-info.darwin-x64.node",
|
||||
"platform-info.darwin-arm64.node",
|
||||
"platform-info.win32-x64-msvc.node",
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
function nativeFileName(platform = process.platform, arch = process.arch) {
|
||||
switch (platform) {
|
||||
case 'darwin':
|
||||
if (arch === 'x64' || arch === 'arm64') return `platform-info.darwin-${arch}.node`;
|
||||
break;
|
||||
case 'win32':
|
||||
if (arch === 'x64' || arch === 'arm64') return `platform-info.win32-${arch}-msvc.node`;
|
||||
break;
|
||||
case 'linux':
|
||||
if (arch === 'x64' || arch === 'arm64') return `platform-info.linux-${arch}-gnu.node`;
|
||||
break;
|
||||
}
|
||||
throw new Error(`Unsupported platform-info target: ${platform}/${arch}`);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
nativeFileName,
|
||||
};
|
||||
@@ -2,19 +2,13 @@
|
||||
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import platformInfo from '../index.js';
|
||||
|
||||
test('loader exposes a stable optional native surface', () => {
|
||||
assert.equal(typeof platformInfo, 'object');
|
||||
assert.equal(platformInfo.getGpuInfo === null || typeof platformInfo.getGpuInfo === 'function', true);
|
||||
assert.equal(platformInfo.loadError === null || platformInfo.loadError instanceof Error, true);
|
||||
});
|
||||
import platformInfoPure from '../pure.cjs';
|
||||
|
||||
test('loader resolves native binaries for supported platforms and architectures', () => {
|
||||
assert.equal(platformInfo._private.nativeFileName('darwin', 'x64'), 'platform-info.darwin-x64.node');
|
||||
assert.equal(platformInfo._private.nativeFileName('darwin', 'arm64'), 'platform-info.darwin-arm64.node');
|
||||
assert.equal(platformInfo._private.nativeFileName('linux', 'x64'), 'platform-info.linux-x64-gnu.node');
|
||||
assert.equal(platformInfo._private.nativeFileName('linux', 'arm64'), 'platform-info.linux-arm64-gnu.node');
|
||||
assert.equal(platformInfo._private.nativeFileName('win32', 'x64'), 'platform-info.win32-x64-msvc.node');
|
||||
assert.equal(platformInfo._private.nativeFileName('win32', 'arm64'), 'platform-info.win32-arm64-msvc.node');
|
||||
assert.equal(platformInfoPure.nativeFileName('darwin', 'x64'), 'platform-info.darwin-x64.node');
|
||||
assert.equal(platformInfoPure.nativeFileName('darwin', 'arm64'), 'platform-info.darwin-arm64.node');
|
||||
assert.equal(platformInfoPure.nativeFileName('linux', 'x64'), 'platform-info.linux-x64-gnu.node');
|
||||
assert.equal(platformInfoPure.nativeFileName('linux', 'arm64'), 'platform-info.linux-arm64-gnu.node');
|
||||
assert.equal(platformInfoPure.nativeFileName('win32', 'x64'), 'platform-info.win32-x64-msvc.node');
|
||||
assert.equal(platformInfoPure.nativeFileName('win32', 'arm64'), 'platform-info.win32-arm64-msvc.node');
|
||||
});
|
||||
|
||||
@@ -193,216 +193,10 @@ fn build_resources(device: &wgpu::Device, config: WgpuStagingConfig) -> WgpuStag
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_acquire_device() -> Option<(wgpu::Device, wgpu::Queue, wgpu::Instance)> {
|
||||
let mut descriptor = wgpu::InstanceDescriptor::new_without_display_handle();
|
||||
descriptor.backends = wgpu::Backends::all() | wgpu::Backends::SECONDARY;
|
||||
let instance = wgpu::Instance::new(descriptor);
|
||||
let adapter = pollster_block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::default(),
|
||||
force_fallback_adapter: false,
|
||||
compatible_surface: None,
|
||||
}))
|
||||
.ok()?;
|
||||
let device_result = pollster_block_on(adapter.request_device(&wgpu::DeviceDescriptor {
|
||||
label: Some("screen_frame_bus.wgpu_staging_backend.test_device"),
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits: wgpu::Limits::default(),
|
||||
memory_hints: wgpu::MemoryHints::default(),
|
||||
trace: wgpu::Trace::Off,
|
||||
experimental_features: wgpu::ExperimentalFeatures::default(),
|
||||
}));
|
||||
match device_result {
|
||||
Ok((device, queue)) => Some((device, queue, instance)),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn pollster_block_on<F: core::future::Future>(fut: F) -> F::Output {
|
||||
futures_executor_block_on(fut)
|
||||
}
|
||||
|
||||
fn futures_executor_block_on<F: core::future::Future>(mut fut: F) -> F::Output {
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
||||
fn raw_waker() -> RawWaker {
|
||||
fn no_op(_: *const ()) {}
|
||||
fn clone(_: *const ()) -> RawWaker {
|
||||
raw_waker()
|
||||
}
|
||||
static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, no_op, no_op, no_op);
|
||||
RawWaker::new(core::ptr::null(), &VTABLE)
|
||||
}
|
||||
let waker = unsafe { Waker::from_raw(raw_waker()) };
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
let mut fut = unsafe { Pin::new_unchecked(&mut fut) };
|
||||
loop {
|
||||
match fut.as_mut().poll(&mut cx) {
|
||||
Poll::Ready(out) => return out,
|
||||
Poll::Pending => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{CpuStagingBackend, STAGING_PAIR_LEN, StagingSurfacePair};
|
||||
use fluxer_gpu_rebuild::{GpuLossRegistry, RebuildOutcome};
|
||||
|
||||
struct GpuCtx {
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
_instance: wgpu::Instance,
|
||||
}
|
||||
|
||||
fn gpu_ctx() -> Option<GpuCtx> {
|
||||
let acquired = std::panic::catch_unwind(std::panic::AssertUnwindSafe(try_acquire_device));
|
||||
let (device, queue, instance) = match acquired {
|
||||
Ok(Some(triple)) => triple,
|
||||
Ok(None) => return None,
|
||||
Err(_) => return None,
|
||||
};
|
||||
Some(GpuCtx {
|
||||
device,
|
||||
queue,
|
||||
_instance: instance,
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn staging_backend_is_ready_only_when_built() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("screen-frame-bus gpu_loss: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let cfg = WgpuStagingConfig::new(256);
|
||||
let mut backend = WgpuStagingBackend::new(&ctx.device, cfg);
|
||||
assert!(
|
||||
GpuLossCallback::is_ready(&backend),
|
||||
"freshly built backend must be ready",
|
||||
);
|
||||
backend.release();
|
||||
assert!(
|
||||
!GpuLossCallback::is_ready(&backend),
|
||||
"released backend must not be ready",
|
||||
);
|
||||
let outcome = backend.rebuild(&ctx.device, &ctx.queue);
|
||||
assert!(outcome.is_ok(), "rebuild on fresh device must succeed");
|
||||
assert!(
|
||||
GpuLossCallback::is_ready(&backend),
|
||||
"rebuilt backend must be ready",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registry_handles_staging_backend_round_trip() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("screen-frame-bus gpu_loss: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let registry = GpuLossRegistry::new();
|
||||
let backend = Box::new(WgpuStagingBackend::new(
|
||||
&ctx.device,
|
||||
WgpuStagingConfig::new(128),
|
||||
));
|
||||
let _guard = registry.register(backend);
|
||||
let report = registry.handle_device_lost(&ctx.device, &ctx.queue);
|
||||
assert_eq!(report.released_count, 1);
|
||||
assert_eq!(report.rebuilt_count, 1);
|
||||
assert_eq!(report.failed_count, 0);
|
||||
assert!(report.is_total_success());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_rebuild_without_release_is_owner_invariant_error() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("screen-frame-bus gpu_loss: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let mut backend = WgpuStagingBackend::new(&ctx.device, WgpuStagingConfig::new(64));
|
||||
let outcome = backend.rebuild(&ctx.device, &ctx.queue);
|
||||
assert!(matches!(
|
||||
outcome,
|
||||
Err(GpuRebuildError::OwnerInvariantBroken { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_between_release_and_rebuild_is_a_noop_and_not_ready() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("screen-frame-bus gpu_loss: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let mut backend = WgpuStagingBackend::new(&ctx.device, WgpuStagingConfig::new(64));
|
||||
backend.release();
|
||||
backend.write(|buf| buf.fill(0xAA));
|
||||
assert!(
|
||||
!<WgpuStagingBackend as StagingBackend>::is_ready(&backend),
|
||||
"write before rebuild must not flip ready",
|
||||
);
|
||||
let observed = backend.read(|buf| buf.len());
|
||||
assert_eq!(observed, 0, "read before rebuild must observe empty mirror");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn surface_pair_with_wgpu_backend_round_trips_cpu_mirror() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("screen-frame-bus gpu_loss: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let cfg = WgpuStagingConfig::new(64);
|
||||
let a = WgpuStagingBackend::new(&ctx.device, cfg);
|
||||
let b = WgpuStagingBackend::new(&ctx.device, cfg);
|
||||
assert_eq!(
|
||||
STAGING_PAIR_LEN, 2,
|
||||
"OBS staging pair is exactly two surfaces"
|
||||
);
|
||||
let mut pair: StagingSurfacePair<WgpuStagingBackend> = StagingSurfacePair::new([a, b]);
|
||||
pair.submit(0, |buf| {
|
||||
buf[0] = 0xDE;
|
||||
buf[1] = 0xAD;
|
||||
})
|
||||
.expect("submit zero must succeed");
|
||||
pair.submit(1, |buf| {
|
||||
buf[0] = 0xBE;
|
||||
buf[1] = 0xEF;
|
||||
})
|
||||
.expect("submit one must succeed");
|
||||
let first = pair
|
||||
.try_map(0, |buf| (buf[0], buf[1]))
|
||||
.expect("map zero ready");
|
||||
assert_eq!(first, (0xDE, 0xAD));
|
||||
let second = pair
|
||||
.try_map(1, |buf| (buf[0], buf[1]))
|
||||
.expect("map one ready");
|
||||
assert_eq!(second, (0xBE, 0xEF));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registry_holds_mixed_packers_and_staging_backends() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("screen-frame-bus gpu_loss: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let registry = GpuLossRegistry::new();
|
||||
let mut guards = Vec::new();
|
||||
for i in 0..4u32 {
|
||||
let bytes = 32u64 << (i % 4);
|
||||
let backend = Box::new(WgpuStagingBackend::new(
|
||||
&ctx.device,
|
||||
WgpuStagingConfig::new(bytes),
|
||||
));
|
||||
guards.push(registry.register(backend));
|
||||
}
|
||||
let report = registry.handle_device_lost(&ctx.device, &ctx.queue);
|
||||
assert_eq!(report.released_count, 4);
|
||||
assert_eq!(report.rebuilt_count, 4);
|
||||
assert_eq!(report.failed_count, 0);
|
||||
for outcome in &report.outcomes {
|
||||
assert!(matches!(outcome, RebuildOutcome::Rebuilt { .. }));
|
||||
}
|
||||
drop(guards);
|
||||
}
|
||||
use super::StagingBackend;
|
||||
use crate::CpuStagingBackend;
|
||||
|
||||
#[test]
|
||||
fn cpu_backend_still_works_alongside_wgpu_backend() {
|
||||
@@ -410,20 +204,4 @@ mod tests {
|
||||
assert!(<CpuStagingBackend as StagingBackend>::is_idle(&cpu));
|
||||
assert!(<CpuStagingBackend as StagingBackend>::is_ready(&cpu));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn buffer_handle_disappears_after_release_and_reappears_after_rebuild() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("screen-frame-bus gpu_loss: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let mut backend = WgpuStagingBackend::new(&ctx.device, WgpuStagingConfig::new(128));
|
||||
assert!(backend.buffer().is_some(), "buffer present after build");
|
||||
backend.release();
|
||||
assert!(backend.buffer().is_none(), "buffer absent after release");
|
||||
backend
|
||||
.rebuild(&ctx.device, &ctx.queue)
|
||||
.expect("rebuild must succeed");
|
||||
assert!(backend.buffer().is_some(), "buffer present after rebuild");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,25 +4,16 @@ const {existsSync} = require('node:fs');
|
||||
const {createHash} = require('node:crypto');
|
||||
const {join, sep} = require('node:path');
|
||||
const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs');
|
||||
const {
|
||||
base64Url,
|
||||
clientDataJSON,
|
||||
nativeFileName,
|
||||
normalizeCreateOptions,
|
||||
normalizeGetOptions,
|
||||
transportBits,
|
||||
} = require('./pure.cjs');
|
||||
const MODULE_NAME = '@fluxer/webauthn';
|
||||
const SKIP_NATIVE_PROBE_ENV = 'FLUXER_WEBAUTHN_SKIP_NATIVE_PROBE';
|
||||
const WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY = 0;
|
||||
const WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM = 1;
|
||||
const WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM = 2;
|
||||
const WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED = 1;
|
||||
const WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED = 2;
|
||||
const WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED = 3;
|
||||
const WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE = 1;
|
||||
const WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT = 2;
|
||||
const WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT = 3;
|
||||
const WEBAUTHN_ENTERPRISE_ATTESTATION_NONE = 0;
|
||||
const WEBAUTHN_ENTERPRISE_ATTESTATION_VENDOR_FACILITATED = 1;
|
||||
const WEBAUTHN_CTAP_TRANSPORT_USB = 0x00000001;
|
||||
const WEBAUTHN_CTAP_TRANSPORT_NFC = 0x00000002;
|
||||
const WEBAUTHN_CTAP_TRANSPORT_BLE = 0x00000004;
|
||||
const WEBAUTHN_CTAP_TRANSPORT_INTERNAL = 0x00000010;
|
||||
const WEBAUTHN_CTAP_TRANSPORT_HYBRID = 0x00000020;
|
||||
const WEBAUTHN_CTAP_TRANSPORT_SMART_CARD = 0x00000040;
|
||||
|
||||
function resolveNativeRoot() {
|
||||
const asarSegment = `${sep}app.asar${sep}`;
|
||||
@@ -31,13 +22,6 @@ function resolveNativeRoot() {
|
||||
return existsSync(unpackedDir) ? unpackedDir : __dirname;
|
||||
}
|
||||
|
||||
function nativeFileName(platform = process.platform, arch = process.arch) {
|
||||
if (platform === 'darwin' && (arch === 'x64' || arch === 'arm64')) return `webauthn.darwin-${arch}.node`;
|
||||
if (platform === 'win32' && (arch === 'x64' || arch === 'arm64')) return `webauthn.win32-${arch}-msvc.node`;
|
||||
if (platform === 'linux' && (arch === 'x64' || arch === 'arm64')) return `webauthn.linux-${arch}-gnu.node`;
|
||||
return null;
|
||||
}
|
||||
|
||||
let binding = null;
|
||||
let loadError = null;
|
||||
|
||||
@@ -79,146 +63,10 @@ if (fileName) {
|
||||
throw loadError;
|
||||
}
|
||||
|
||||
function base64Url(buffer) {
|
||||
return Buffer.from(buffer).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||
}
|
||||
|
||||
function normalizedOrigin(origin) {
|
||||
if (typeof origin !== 'string' || origin.length === 0) {
|
||||
throw new TypeError('@fluxer/webauthn requires an origin string');
|
||||
}
|
||||
return new URL(origin).origin;
|
||||
}
|
||||
|
||||
function relyingPartyId(options, origin) {
|
||||
if (typeof options.rpId === 'string' && options.rpId.length > 0) return options.rpId;
|
||||
if (options.rp && typeof options.rp.id === 'string' && options.rp.id.length > 0) return options.rp.id;
|
||||
return new URL(origin).hostname;
|
||||
}
|
||||
|
||||
function clientDataJSON(type, challenge, origin) {
|
||||
return Buffer.from(
|
||||
JSON.stringify({
|
||||
type,
|
||||
challenge: base64Url(challenge),
|
||||
origin,
|
||||
crossOrigin: false,
|
||||
}),
|
||||
'utf8',
|
||||
);
|
||||
}
|
||||
|
||||
function clientDataHash(data) {
|
||||
return createHash('sha256').update(data).digest();
|
||||
}
|
||||
|
||||
function authenticatorAttachment(value) {
|
||||
if (value === 'platform') return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM;
|
||||
if (value === 'cross-platform') return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM;
|
||||
return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;
|
||||
}
|
||||
|
||||
function userVerification(value) {
|
||||
if (value === 'required') return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED;
|
||||
if (value === 'discouraged') return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED;
|
||||
return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED;
|
||||
}
|
||||
|
||||
function attestation(value) {
|
||||
if (value === 'direct') return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT;
|
||||
if (value === 'indirect') return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT;
|
||||
return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE;
|
||||
}
|
||||
|
||||
function enterpriseAttestation(value) {
|
||||
return value === 'enterprise'
|
||||
? WEBAUTHN_ENTERPRISE_ATTESTATION_VENDOR_FACILITATED
|
||||
: WEBAUTHN_ENTERPRISE_ATTESTATION_NONE;
|
||||
}
|
||||
|
||||
function transportBits(transports) {
|
||||
if (!Array.isArray(transports)) return 0;
|
||||
let bits = 0;
|
||||
for (const transport of transports) {
|
||||
if (transport === 'usb') bits |= WEBAUTHN_CTAP_TRANSPORT_USB;
|
||||
else if (transport === 'nfc') bits |= WEBAUTHN_CTAP_TRANSPORT_NFC;
|
||||
else if (transport === 'ble') bits |= WEBAUTHN_CTAP_TRANSPORT_BLE;
|
||||
else if (transport === 'internal') bits |= WEBAUTHN_CTAP_TRANSPORT_INTERNAL;
|
||||
else if (transport === 'hybrid') bits |= WEBAUTHN_CTAP_TRANSPORT_HYBRID;
|
||||
else if (transport === 'smart-card') bits |= WEBAUTHN_CTAP_TRANSPORT_SMART_CARD;
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
function credentialDescriptors(descriptors) {
|
||||
if (!Array.isArray(descriptors)) return [];
|
||||
return descriptors.map((descriptor) => ({
|
||||
id: Buffer.from(descriptor.id),
|
||||
transports: transportBits(descriptor.transports),
|
||||
}));
|
||||
}
|
||||
|
||||
function windowHandleBuffer(value) {
|
||||
if (Buffer.isBuffer(value)) return value;
|
||||
if (value instanceof Uint8Array) return Buffer.from(value);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function pinString(value) {
|
||||
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
||||
}
|
||||
|
||||
function residentKeyFlags(selection) {
|
||||
const residentKey = selection?.residentKey;
|
||||
const requireResidentKey = Boolean(selection?.requireResidentKey) || residentKey === 'required';
|
||||
return {
|
||||
requireResidentKey,
|
||||
preferResidentKey: requireResidentKey || residentKey === 'preferred',
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeCreateOptions(options) {
|
||||
if (!options || typeof options !== 'object') throw new TypeError('registration options must be an object');
|
||||
const origin = normalizedOrigin(options.origin);
|
||||
const selection = options.authenticatorSelection || {};
|
||||
const residentKeys = residentKeyFlags(selection);
|
||||
return {
|
||||
rpId: relyingPartyId(options, origin),
|
||||
rpName: options.rp && typeof options.rp.name === 'string' ? options.rp.name : relyingPartyId(options, origin),
|
||||
challenge: Buffer.from(options.challenge),
|
||||
userId: Buffer.from(options.user.id),
|
||||
userName: options.user.name,
|
||||
userDisplayName: options.user.displayName,
|
||||
clientDataJSON: clientDataJSON('webauthn.create', options.challenge, origin),
|
||||
pubKeyCredParams: options.pubKeyCredParams,
|
||||
excludeCredentials: credentialDescriptors(options.excludeCredentials),
|
||||
timeout: Number.isFinite(options.timeout) ? Math.max(0, Math.trunc(options.timeout)) : 0,
|
||||
authenticatorAttachment: authenticatorAttachment(selection.authenticatorAttachment),
|
||||
userVerification: userVerification(selection.userVerification),
|
||||
attestation: attestation(options.attestation),
|
||||
enterpriseAttestation: enterpriseAttestation(options.attestation),
|
||||
windowHandle: windowHandleBuffer(options.windowHandle),
|
||||
pin: pinString(options.pin),
|
||||
...residentKeys,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeGetOptions(options) {
|
||||
if (!options || typeof options !== 'object') throw new TypeError('assertion options must be an object');
|
||||
const origin = normalizedOrigin(options.origin);
|
||||
return {
|
||||
rpId: relyingPartyId(options, origin),
|
||||
challenge: Buffer.from(options.challenge),
|
||||
clientDataJSON: clientDataJSON('webauthn.get', options.challenge, origin),
|
||||
allowCredentials: credentialDescriptors(options.allowCredentials),
|
||||
timeout: Number.isFinite(options.timeout) ? Math.max(0, Math.trunc(options.timeout)) : 0,
|
||||
authenticatorAttachment: WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY,
|
||||
userVerification: userVerification(options.userVerification),
|
||||
windowHandle: windowHandleBuffer(options.windowHandle),
|
||||
pin: pinString(options.pin),
|
||||
};
|
||||
}
|
||||
|
||||
function unavailableError() {
|
||||
return new Error(
|
||||
`@fluxer/webauthn native backend unavailable on ${process.platform}/${process.arch}: ${
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"index.js",
|
||||
"index.d.ts",
|
||||
"loader-diagnostics.cjs",
|
||||
"pure.cjs",
|
||||
"*.so*",
|
||||
"webauthn.linux-x64-gnu.node",
|
||||
"webauthn.linux-arm64-gnu.node",
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
const WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY = 0;
|
||||
const WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM = 1;
|
||||
const WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM = 2;
|
||||
const WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED = 1;
|
||||
const WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED = 2;
|
||||
const WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED = 3;
|
||||
const WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE = 1;
|
||||
const WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT = 2;
|
||||
const WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT = 3;
|
||||
const WEBAUTHN_ENTERPRISE_ATTESTATION_NONE = 0;
|
||||
const WEBAUTHN_ENTERPRISE_ATTESTATION_VENDOR_FACILITATED = 1;
|
||||
const WEBAUTHN_CTAP_TRANSPORT_USB = 0x00000001;
|
||||
const WEBAUTHN_CTAP_TRANSPORT_NFC = 0x00000002;
|
||||
const WEBAUTHN_CTAP_TRANSPORT_BLE = 0x00000004;
|
||||
const WEBAUTHN_CTAP_TRANSPORT_INTERNAL = 0x00000010;
|
||||
const WEBAUTHN_CTAP_TRANSPORT_HYBRID = 0x00000020;
|
||||
const WEBAUTHN_CTAP_TRANSPORT_SMART_CARD = 0x00000040;
|
||||
|
||||
function nativeFileName(platform = process.platform, arch = process.arch) {
|
||||
if (platform === 'darwin' && (arch === 'x64' || arch === 'arm64')) return `webauthn.darwin-${arch}.node`;
|
||||
if (platform === 'win32' && (arch === 'x64' || arch === 'arm64')) return `webauthn.win32-${arch}-msvc.node`;
|
||||
if (platform === 'linux' && (arch === 'x64' || arch === 'arm64')) return `webauthn.linux-${arch}-gnu.node`;
|
||||
return null;
|
||||
}
|
||||
|
||||
function base64Url(buffer) {
|
||||
return Buffer.from(buffer).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||
}
|
||||
|
||||
function normalizedOrigin(origin) {
|
||||
if (typeof origin !== 'string' || origin.length === 0) {
|
||||
throw new TypeError('@fluxer/webauthn requires an origin string');
|
||||
}
|
||||
return new URL(origin).origin;
|
||||
}
|
||||
|
||||
function relyingPartyId(options, origin) {
|
||||
if (typeof options.rpId === 'string' && options.rpId.length > 0) return options.rpId;
|
||||
if (options.rp && typeof options.rp.id === 'string' && options.rp.id.length > 0) return options.rp.id;
|
||||
return new URL(origin).hostname;
|
||||
}
|
||||
|
||||
function clientDataJSON(type, challenge, origin) {
|
||||
return Buffer.from(
|
||||
JSON.stringify({
|
||||
type,
|
||||
challenge: base64Url(challenge),
|
||||
origin,
|
||||
crossOrigin: false,
|
||||
}),
|
||||
'utf8',
|
||||
);
|
||||
}
|
||||
|
||||
function authenticatorAttachment(value) {
|
||||
if (value === 'platform') return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM;
|
||||
if (value === 'cross-platform') return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM;
|
||||
return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;
|
||||
}
|
||||
|
||||
function userVerification(value) {
|
||||
if (value === 'required') return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED;
|
||||
if (value === 'discouraged') return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED;
|
||||
return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED;
|
||||
}
|
||||
|
||||
function attestation(value) {
|
||||
if (value === 'direct') return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT;
|
||||
if (value === 'indirect') return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT;
|
||||
return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE;
|
||||
}
|
||||
|
||||
function enterpriseAttestation(value) {
|
||||
return value === 'enterprise'
|
||||
? WEBAUTHN_ENTERPRISE_ATTESTATION_VENDOR_FACILITATED
|
||||
: WEBAUTHN_ENTERPRISE_ATTESTATION_NONE;
|
||||
}
|
||||
|
||||
function transportBits(transports) {
|
||||
if (!Array.isArray(transports)) return 0;
|
||||
let bits = 0;
|
||||
for (const transport of transports) {
|
||||
if (transport === 'usb') bits |= WEBAUTHN_CTAP_TRANSPORT_USB;
|
||||
else if (transport === 'nfc') bits |= WEBAUTHN_CTAP_TRANSPORT_NFC;
|
||||
else if (transport === 'ble') bits |= WEBAUTHN_CTAP_TRANSPORT_BLE;
|
||||
else if (transport === 'internal') bits |= WEBAUTHN_CTAP_TRANSPORT_INTERNAL;
|
||||
else if (transport === 'hybrid') bits |= WEBAUTHN_CTAP_TRANSPORT_HYBRID;
|
||||
else if (transport === 'smart-card') bits |= WEBAUTHN_CTAP_TRANSPORT_SMART_CARD;
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
function credentialDescriptors(descriptors) {
|
||||
if (!Array.isArray(descriptors)) return [];
|
||||
return descriptors.map((descriptor) => ({
|
||||
id: Buffer.from(descriptor.id),
|
||||
transports: transportBits(descriptor.transports),
|
||||
}));
|
||||
}
|
||||
|
||||
function windowHandleBuffer(value) {
|
||||
if (Buffer.isBuffer(value)) return value;
|
||||
if (value instanceof Uint8Array) return Buffer.from(value);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function pinString(value) {
|
||||
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
||||
}
|
||||
|
||||
function residentKeyFlags(selection) {
|
||||
const residentKey = selection?.residentKey;
|
||||
const requireResidentKey = Boolean(selection?.requireResidentKey) || residentKey === 'required';
|
||||
return {
|
||||
requireResidentKey,
|
||||
preferResidentKey: requireResidentKey || residentKey === 'preferred',
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeCreateOptions(options) {
|
||||
if (!options || typeof options !== 'object') throw new TypeError('registration options must be an object');
|
||||
const origin = normalizedOrigin(options.origin);
|
||||
const selection = options.authenticatorSelection || {};
|
||||
const residentKeys = residentKeyFlags(selection);
|
||||
return {
|
||||
rpId: relyingPartyId(options, origin),
|
||||
rpName: options.rp && typeof options.rp.name === 'string' ? options.rp.name : relyingPartyId(options, origin),
|
||||
challenge: Buffer.from(options.challenge),
|
||||
userId: Buffer.from(options.user.id),
|
||||
userName: options.user.name,
|
||||
userDisplayName: options.user.displayName,
|
||||
clientDataJSON: clientDataJSON('webauthn.create', options.challenge, origin),
|
||||
pubKeyCredParams: options.pubKeyCredParams,
|
||||
excludeCredentials: credentialDescriptors(options.excludeCredentials),
|
||||
timeout: Number.isFinite(options.timeout) ? Math.max(0, Math.trunc(options.timeout)) : 0,
|
||||
authenticatorAttachment: authenticatorAttachment(selection.authenticatorAttachment),
|
||||
userVerification: userVerification(selection.userVerification),
|
||||
attestation: attestation(options.attestation),
|
||||
enterpriseAttestation: enterpriseAttestation(options.attestation),
|
||||
windowHandle: windowHandleBuffer(options.windowHandle),
|
||||
pin: pinString(options.pin),
|
||||
...residentKeys,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeGetOptions(options) {
|
||||
if (!options || typeof options !== 'object') throw new TypeError('assertion options must be an object');
|
||||
const origin = normalizedOrigin(options.origin);
|
||||
return {
|
||||
rpId: relyingPartyId(options, origin),
|
||||
challenge: Buffer.from(options.challenge),
|
||||
clientDataJSON: clientDataJSON('webauthn.get', options.challenge, origin),
|
||||
allowCredentials: credentialDescriptors(options.allowCredentials),
|
||||
timeout: Number.isFinite(options.timeout) ? Math.max(0, Math.trunc(options.timeout)) : 0,
|
||||
authenticatorAttachment: WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY,
|
||||
userVerification: userVerification(options.userVerification),
|
||||
windowHandle: windowHandleBuffer(options.windowHandle),
|
||||
pin: pinString(options.pin),
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
base64Url,
|
||||
clientDataJSON,
|
||||
nativeFileName,
|
||||
normalizeCreateOptions,
|
||||
normalizeGetOptions,
|
||||
transportBits,
|
||||
};
|
||||
@@ -2,37 +2,21 @@
|
||||
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import webauthn from '../index.js';
|
||||
import webauthnPure from '../pure.cjs';
|
||||
|
||||
test('loader resolves supported native filenames', () => {
|
||||
assert.equal(webauthn._private.nativeFileName('darwin', 'x64'), 'webauthn.darwin-x64.node');
|
||||
assert.equal(webauthn._private.nativeFileName('darwin', 'arm64'), 'webauthn.darwin-arm64.node');
|
||||
assert.equal(webauthn._private.nativeFileName('linux', 'x64'), 'webauthn.linux-x64-gnu.node');
|
||||
assert.equal(webauthn._private.nativeFileName('linux', 'arm64'), 'webauthn.linux-arm64-gnu.node');
|
||||
assert.equal(webauthn._private.nativeFileName('win32', 'x64'), 'webauthn.win32-x64-msvc.node');
|
||||
assert.equal(webauthn._private.nativeFileName('win32', 'arm64'), 'webauthn.win32-arm64-msvc.node');
|
||||
assert.equal(webauthn._private.nativeFileName('freebsd', 'x64'), null);
|
||||
});
|
||||
|
||||
test('loader exposes the Fluxer WebAuthn surface', async () => {
|
||||
assert.equal(typeof webauthn.create, 'function');
|
||||
assert.equal(typeof webauthn.get, 'function');
|
||||
assert.equal(typeof webauthn.getBackendInfo, 'function');
|
||||
assert.equal(typeof webauthn.isSupported, 'function');
|
||||
const info = webauthn.getBackendInfo();
|
||||
const supported = await webauthn.isSupported();
|
||||
assert.equal(typeof supported, 'boolean');
|
||||
assert.equal(supported, info.supported);
|
||||
assert.equal(typeof info, 'object');
|
||||
assert.equal(typeof info.target, 'string');
|
||||
assert.equal(typeof info.backend, 'string');
|
||||
assert.equal(typeof info.supported, 'boolean');
|
||||
assert.equal(typeof info.ceremoniesImplemented, 'boolean');
|
||||
assert.equal(webauthnPure.nativeFileName('darwin', 'x64'), 'webauthn.darwin-x64.node');
|
||||
assert.equal(webauthnPure.nativeFileName('darwin', 'arm64'), 'webauthn.darwin-arm64.node');
|
||||
assert.equal(webauthnPure.nativeFileName('linux', 'x64'), 'webauthn.linux-x64-gnu.node');
|
||||
assert.equal(webauthnPure.nativeFileName('linux', 'arm64'), 'webauthn.linux-arm64-gnu.node');
|
||||
assert.equal(webauthnPure.nativeFileName('win32', 'x64'), 'webauthn.win32-x64-msvc.node');
|
||||
assert.equal(webauthnPure.nativeFileName('win32', 'arm64'), 'webauthn.win32-arm64-msvc.node');
|
||||
assert.equal(webauthnPure.nativeFileName('freebsd', 'x64'), null);
|
||||
});
|
||||
|
||||
test('normalization creates spec-shaped client data and Windows transport bits', () => {
|
||||
const challenge = Buffer.from([1, 2, 3, 4]);
|
||||
const normalized = webauthn._private.normalizeCreateOptions({
|
||||
const normalized = webauthnPure.normalizeCreateOptions({
|
||||
origin: 'https://web.canary.fluxer.app/channels/@me',
|
||||
challenge,
|
||||
rp: {id: 'fluxer.app', name: 'Fluxer'},
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Command,
|
||||
[string]$OutFile = 'C:\tools\vm_test_out.txt',
|
||||
[int]$TimeoutSec = 120
|
||||
)
|
||||
|
||||
$taskName = 'FluxerVmTest'
|
||||
if (Test-Path $OutFile) { Remove-Item -Force $OutFile }
|
||||
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
|
||||
|
||||
$action = New-ScheduledTaskAction -Execute 'cmd.exe' -Argument ('/c ' + $Command + ' > "' + $OutFile + '" 2>&1')
|
||||
$principal = New-ScheduledTaskPrincipal -UserId 'hampus' -LogonType Interactive
|
||||
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit (New-TimeSpan -Minutes 10)
|
||||
Register-ScheduledTask -TaskName $taskName -Action $action -Principal $principal -Settings $settings -Force | Out-Null
|
||||
Start-ScheduledTask -TaskName $taskName
|
||||
|
||||
$deadline = (Get-Date).AddSeconds($TimeoutSec)
|
||||
do {
|
||||
Start-Sleep -Milliseconds 500
|
||||
$state = (Get-ScheduledTask -TaskName $taskName).State
|
||||
} while ($state -ne 'Ready' -and (Get-Date) -lt $deadline)
|
||||
|
||||
$info = Get-ScheduledTaskInfo -TaskName $taskName
|
||||
Write-Output ('TASK-STATE: ' + $state + ' LAST-RESULT: ' + $info.LastTaskResult)
|
||||
if (Test-Path $OutFile) {
|
||||
Write-Output '--- OUTPUT ---'
|
||||
Get-Content $OutFile
|
||||
} else {
|
||||
Write-Output 'NO OUTPUT FILE'
|
||||
}
|
||||
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
|
||||
@@ -1,22 +0,0 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import ctypes
|
||||
import sys
|
||||
|
||||
device_path = sys.argv[1].encode() if len(sys.argv) > 1 else b"/dev/hidraw4"
|
||||
pin = sys.argv[2].encode() if len(sys.argv) > 2 else b"123456"
|
||||
|
||||
lib = ctypes.CDLL("libfido2.so.1")
|
||||
lib.fido_init(0)
|
||||
lib.fido_dev_new.restype = ctypes.c_void_p
|
||||
lib.fido_strerr.restype = ctypes.c_char_p
|
||||
|
||||
dev = lib.fido_dev_new()
|
||||
rc = lib.fido_dev_open(ctypes.c_void_p(dev), device_path)
|
||||
if rc != 0:
|
||||
print(f"open failed rc={rc} {lib.fido_strerr(rc).decode()}")
|
||||
sys.exit(1)
|
||||
rc = lib.fido_dev_set_pin(ctypes.c_void_p(dev), pin, None)
|
||||
print(f"set_pin rc={rc} {lib.fido_strerr(rc).decode()}")
|
||||
lib.fido_dev_close(ctypes.c_void_p(dev))
|
||||
sys.exit(0 if rc == 0 else 1)
|
||||
@@ -1,89 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
virtual_fido "github.com/bulwarkid/virtual-fido"
|
||||
"github.com/bulwarkid/virtual-fido/fido_client"
|
||||
)
|
||||
|
||||
type autoApproveSupport struct {
|
||||
vaultFilename string
|
||||
}
|
||||
|
||||
func (support *autoApproveSupport) ApproveClientAction(action fido_client.ClientAction, params fido_client.ClientActionRequestParams) bool {
|
||||
fmt.Printf("auto-approving action=%d relyingParty=%q user=%q\n", action, params.RelyingParty, params.UserName)
|
||||
return true
|
||||
}
|
||||
|
||||
func (support *autoApproveSupport) SaveData(data []byte) {
|
||||
err := os.WriteFile(support.vaultFilename, data, 0o600)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("could not write vault: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
func (support *autoApproveSupport) RetrieveData() []byte {
|
||||
f, err := os.Open(support.vaultFilename)
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("could not open vault: %s", err))
|
||||
}
|
||||
defer f.Close()
|
||||
data, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("could not read vault: %s", err))
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (support *autoApproveSupport) Passphrase() string {
|
||||
return "vm-test-passphrase"
|
||||
}
|
||||
|
||||
func main() {
|
||||
vault := os.Getenv("VFIDO_VAULT")
|
||||
if vault == "" {
|
||||
vault = "/tmp/vfido-vault.json"
|
||||
}
|
||||
authority := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(0),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Fluxer VM Test Virtual FIDO"},
|
||||
Country: []string{"US"},
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||
IsCA: true,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
authorityCertBytes, err := x509.CreateCertificate(rand.Reader, authority, authority, &privateKey.PublicKey, privateKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
encryptionKey := sha256.Sum256([]byte("fluxer-vm-test"))
|
||||
|
||||
virtual_fido.SetLogOutput(os.Stdout)
|
||||
support := &autoApproveSupport{vaultFilename: vault}
|
||||
client := fido_client.NewDefaultClient(authorityCertBytes, privateKey, encryptionKey, support, support)
|
||||
fmt.Println("virtual-fido USBIP server starting on 127.0.0.1:3240 (bus 2-2)")
|
||||
virtual_fido.Start(client)
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon",
|
||||
"test": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- test-webrtc-sender-rust && node --test index.test.mjs scripts/livekit-harness.test.mjs",
|
||||
"test:livekit": "node scripts/livekit-harness.mjs"
|
||||
"test": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- test-webrtc-sender-rust && node --test index.test.mjs"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,842 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import assert from 'node:assert/strict';
|
||||
import {describe, test} from 'node:test';
|
||||
import {
|
||||
buildReport,
|
||||
buildScenarioConfigs,
|
||||
createLiveKitAdminToken,
|
||||
expectedCodecMime,
|
||||
jwtSubject,
|
||||
liveKitApiUrl,
|
||||
liveKitTcpTarget,
|
||||
parseCodecList,
|
||||
parseConfig,
|
||||
sanitizeConfig,
|
||||
scenarioRoomName,
|
||||
serverPublishedTrackChecks,
|
||||
statsHasOutbound,
|
||||
statsHasOutboundCodec,
|
||||
strictFailures,
|
||||
} from './livekit-harness.mjs';
|
||||
|
||||
function tokenForSubject(subject) {
|
||||
const payload = Buffer.from(JSON.stringify({sub: subject}), 'utf8').toString('base64url');
|
||||
return `header.${payload}.signature`;
|
||||
}
|
||||
|
||||
function jwtPayload(token) {
|
||||
const parts = token.split('.');
|
||||
return JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
|
||||
}
|
||||
|
||||
async function withHarnessEnv(env, callback) {
|
||||
const previous = {...process.env};
|
||||
for (const key of Object.keys(process.env)) {
|
||||
if (
|
||||
key.startsWith('LIVEKIT_') ||
|
||||
key.startsWith('FLUXER_WEBRTC_SENDER_LIVEKIT') ||
|
||||
key.startsWith('FLUXER_NATIVE_MEDIA')
|
||||
) {
|
||||
delete process.env[key];
|
||||
}
|
||||
}
|
||||
Object.assign(process.env, env);
|
||||
try {
|
||||
return await callback();
|
||||
} finally {
|
||||
for (const key of Object.keys(process.env)) {
|
||||
delete process.env[key];
|
||||
}
|
||||
Object.assign(process.env, previous);
|
||||
}
|
||||
}
|
||||
|
||||
function strictReportFor(
|
||||
config,
|
||||
{
|
||||
videoFrameTimes = null,
|
||||
videoFrameRecords = null,
|
||||
audioFrameTimes = [],
|
||||
droppedVideoFrameCallbacks = 0,
|
||||
subscriberStatsSamples = [],
|
||||
} = {},
|
||||
) {
|
||||
const strictStartedAtMs = 1_000;
|
||||
const strictEndedAtMs = 2_000;
|
||||
const effectiveVideoFrameRecords =
|
||||
videoFrameRecords ??
|
||||
(videoFrameTimes ?? []).map((atMs) => ({
|
||||
atMs,
|
||||
identity: config.publisherIdentity,
|
||||
kind: 'video',
|
||||
source: 'screen_share',
|
||||
trackName: 'screen',
|
||||
trackSid: 'TR_screen',
|
||||
width: config.expectedWidth,
|
||||
height: config.expectedHeight,
|
||||
}));
|
||||
const effectiveVideoFrameTimes = videoFrameTimes ?? effectiveVideoFrameRecords.map((record) => record.atMs);
|
||||
return buildReport({
|
||||
status: 'pass',
|
||||
config,
|
||||
context: {
|
||||
startedAtMs: strictStartedAtMs - 500,
|
||||
startedAtIso: new Date(strictStartedAtMs - 500).toISOString(),
|
||||
strictStartedAtMs,
|
||||
strictEndedAtMs,
|
||||
publisherState: {statsSamples: [], stats: null},
|
||||
subscriberState: {
|
||||
statsSamples: subscriberStatsSamples,
|
||||
stats: subscriberStatsSamples.at(-1)?.payload ?? null,
|
||||
videoCallbacks: effectiveVideoFrameTimes.length,
|
||||
videoBytes: effectiveVideoFrameTimes.length * 4,
|
||||
videoFrameTimes: effectiveVideoFrameTimes,
|
||||
videoFrameRecords: effectiveVideoFrameRecords,
|
||||
audioFrameTimes,
|
||||
lastVideoMeta: effectiveVideoFrameRecords.at(-1) ?? null,
|
||||
lastVideoFrameAtMs: effectiveVideoFrameTimes.at(-1) ?? null,
|
||||
lastAudioFrameAtMs: audioFrameTimes.at(-1) ?? null,
|
||||
},
|
||||
subscriber: {
|
||||
inboundAudioFrames: () => audioFrameTimes.length,
|
||||
inboundVideoFrames: () => effectiveVideoFrameTimes.length,
|
||||
droppedVideoFrameCallbacks: () => droppedVideoFrameCallbacks,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function findCheck(checks, name) {
|
||||
const check = checks.find((entry) => entry.name === name);
|
||||
assert.ok(check, `missing check: ${name}`);
|
||||
return check;
|
||||
}
|
||||
|
||||
function serverChecksFor(config, serverState) {
|
||||
return serverPublishedTrackChecks({
|
||||
config,
|
||||
serverState,
|
||||
publisherIdentity: config.publisherIdentity,
|
||||
secondaryPublisherIdentity: config.secondaryPublisherIdentity,
|
||||
});
|
||||
}
|
||||
|
||||
describe('livekit harness setup matrix', () => {
|
||||
test('normalizes LiveKit screenshare source spelling in outbound stats', () => {
|
||||
const stats = {
|
||||
outbound: [
|
||||
{
|
||||
trackSid: 'TR_screen',
|
||||
source: 'screenshare',
|
||||
kind: 'video',
|
||||
codec: 'video/VP8',
|
||||
bitrateKbps: 400,
|
||||
packetsLost: 0,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
assert.equal(statsHasOutbound(stats, 'video', 'screen_share'), true);
|
||||
assert.equal(statsHasOutboundCodec(stats, 'video', 'screen_share', 'video/VP8'), true);
|
||||
assert.equal(statsHasOutboundCodec(stats, 'video', 'screen_share', 'video/H264'), false);
|
||||
});
|
||||
|
||||
test('maps all supported codec spellings to expected mime types', () => {
|
||||
assert.equal(expectedCodecMime('vp8'), 'video/VP8');
|
||||
assert.equal(expectedCodecMime('VP9'), 'video/VP9');
|
||||
assert.equal(expectedCodecMime('h264'), 'video/H264');
|
||||
assert.equal(expectedCodecMime('av1'), 'video/AV1');
|
||||
assert.equal(expectedCodecMime('h265'), 'video/H265');
|
||||
assert.equal(expectedCodecMime('hevc'), 'video/H265');
|
||||
assert.equal(expectedCodecMime(''), null);
|
||||
assert.throws(() => expectedCodecMime('h266'), /unsupported/);
|
||||
});
|
||||
|
||||
test('parses codec lists with fallback and rejects empty effective lists', () => {
|
||||
assert.deepEqual(parseCodecList(' vp8, h264,, hevc ', 'av1'), ['vp8', 'h264', 'hevc']);
|
||||
assert.deepEqual(parseCodecList('', 'vp9'), ['vp9']);
|
||||
assert.throws(() => parseCodecList('', ''), /at least one/);
|
||||
assert.throws(() => parseCodecList('vp8,h266', 'vp8'), /unsupported/);
|
||||
});
|
||||
|
||||
test('default non-strict config keeps the fast local live setup small', async () => {
|
||||
await withHarnessEnv({}, () => {
|
||||
const config = parseConfig();
|
||||
assert.equal(config.url, 'ws://localhost:7880');
|
||||
assert.equal(config.serverApiUrl, 'http://localhost:7880');
|
||||
assert.deepEqual(config.screenCodecs, ['vp8']);
|
||||
assert.equal(config.expectedScreenCodec, 'video/VP8');
|
||||
assert.equal(config.secondaryPublisher, false);
|
||||
assert.equal(config.microphone, true);
|
||||
assert.equal(config.screenAudio, false);
|
||||
assert.equal(config.dataPacket, false);
|
||||
assert.equal(config.subscriptionCycle, false);
|
||||
assert.equal(config.validateServerPublishing, false);
|
||||
assert.equal(config.durationMs, 0);
|
||||
assert.equal(config.externalTokens, false);
|
||||
assert.equal(config.screenSimulcast, true);
|
||||
assert.equal(config.secondaryPublisherScreenSimulcast, true);
|
||||
assert.equal(config.adaptiveSend, true);
|
||||
assert.equal(config.minVideoFps, 15);
|
||||
assert.equal(config.minResolutionScale, 0.5);
|
||||
assert.equal(config.maxAudioBufferMs, 750);
|
||||
});
|
||||
});
|
||||
|
||||
test('strict config expands to multi-codec secondary-publisher scenarios', async () => {
|
||||
await withHarnessEnv(
|
||||
{
|
||||
LIVEKIT_HARNESS_STRICT: '1',
|
||||
LIVEKIT_ROOM: 'matrix-room',
|
||||
LIVEKIT_SCREEN_CODECS: 'vp8,h264,hevc',
|
||||
LIVEKIT_EXPECT_SCREEN_CODECS: 'vp8,h264,h265',
|
||||
},
|
||||
() => {
|
||||
const config = parseConfig();
|
||||
assert.equal(config.secondaryPublisher, true);
|
||||
assert.equal(config.screenAudio, true);
|
||||
assert.equal(config.dataPacket, true);
|
||||
assert.equal(config.subscriptionCycle, true);
|
||||
assert.equal(config.validateServerPublishing, true);
|
||||
assert.equal(config.durationMs, 10 * 60 * 1000);
|
||||
assert.equal(config.maxAudioFrameGapMs, 250);
|
||||
assert.equal(config.maxPacketLoss, null);
|
||||
assert.equal(config.requireStableResolution, true);
|
||||
assert.equal(config.videoPattern, 'gradient');
|
||||
|
||||
const scenarios = buildScenarioConfigs(config);
|
||||
assert.deepEqual(
|
||||
scenarios.map((scenario) => scenario.scenarioName),
|
||||
['codec-vp8-with-h264', 'codec-h264-with-hevc', 'codec-hevc-with-vp8'],
|
||||
);
|
||||
assert.deepEqual(
|
||||
scenarios.map((scenario) => scenario.expectedScreenCodec),
|
||||
['video/VP8', 'video/H264', 'video/H265'],
|
||||
);
|
||||
assert.deepEqual(
|
||||
scenarios.map((scenario) => scenario.expectedSecondaryPublisherCodec),
|
||||
['video/H264', 'video/H265', 'video/VP8'],
|
||||
);
|
||||
assert.equal(scenarios[0].room, 'matrix-room-codec-vp8-with-h264');
|
||||
assert.equal(scenarios[1].reportPath, null);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('stress knobs parse packet loss, resolution stability, audio gap, and video pattern', async () => {
|
||||
await withHarnessEnv(
|
||||
{
|
||||
LIVEKIT_MAX_PACKET_LOSS: '0',
|
||||
LIVEKIT_REQUIRE_STABLE_RESOLUTION: '0',
|
||||
LIVEKIT_MAX_AUDIO_FRAME_GAP_MS: '180',
|
||||
LIVEKIT_VIDEO_PATTERN: 'fast',
|
||||
LIVEKIT_VIDEO_INPUT: 'nv12',
|
||||
LIVEKIT_SUBSCRIBER_VIDEO_QUALITY: 'high',
|
||||
LIVEKIT_SCREEN_SIMULCAST: '0',
|
||||
LIVEKIT_SECOND_PUBLISHER_SCREEN_SIMULCAST: '1',
|
||||
LIVEKIT_SCREEN_FPS: '60',
|
||||
LIVEKIT_ADAPTIVE_SEND: '0',
|
||||
LIVEKIT_MIN_VIDEO_FPS: '24',
|
||||
LIVEKIT_MIN_RESOLUTION_SCALE: '0.75',
|
||||
LIVEKIT_MAX_AUDIO_BUFFER_MS: '640',
|
||||
},
|
||||
() => {
|
||||
const config = parseConfig();
|
||||
assert.equal(config.maxPacketLoss, 0);
|
||||
assert.equal(config.requireStableResolution, false);
|
||||
assert.equal(config.maxAudioFrameGapMs, 180);
|
||||
assert.equal(config.videoPattern, 'fast');
|
||||
assert.equal(config.videoInput, 'nv12');
|
||||
assert.equal(config.subscriberVideoQuality, 'high');
|
||||
assert.equal(config.screenSimulcast, false);
|
||||
assert.equal(config.secondaryPublisherScreenSimulcast, true);
|
||||
assert.equal(config.adaptiveSend, false);
|
||||
assert.equal(config.minVideoFps, 24);
|
||||
assert.equal(config.minResolutionScale, 0.75);
|
||||
assert.equal(config.maxAudioBufferMs, 640);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('stress knobs reject impossible send pacing configurations', async () => {
|
||||
await withHarnessEnv({LIVEKIT_SCREEN_FPS: '0'}, () =>
|
||||
assert.throws(() => parseConfig(), /LIVEKIT_SCREEN_FPS must be a positive number/),
|
||||
);
|
||||
await withHarnessEnv({LIVEKIT_MIN_VIDEO_FPS: '0'}, () =>
|
||||
assert.throws(() => parseConfig(), /LIVEKIT_MIN_VIDEO_FPS must be a positive number/),
|
||||
);
|
||||
await withHarnessEnv({LIVEKIT_SCREEN_FPS: '30', LIVEKIT_MIN_VIDEO_FPS: '60'}, () =>
|
||||
assert.throws(() => parseConfig(), /MIN_VIDEO_FPS must be less than or equal/),
|
||||
);
|
||||
await withHarnessEnv({LIVEKIT_MAX_AUDIO_BUFFER_MS: '0'}, () =>
|
||||
assert.throws(() => parseConfig(), /LIVEKIT_MAX_AUDIO_BUFFER_MS must be a positive integer/),
|
||||
);
|
||||
await withHarnessEnv({LIVEKIT_MIN_RESOLUTION_SCALE: '0'}, () =>
|
||||
assert.throws(() => parseConfig(), /LIVEKIT_MIN_RESOLUTION_SCALE must be a positive number/),
|
||||
);
|
||||
await withHarnessEnv({LIVEKIT_MIN_RESOLUTION_SCALE: '1.5'}, () =>
|
||||
assert.throws(() => parseConfig(), /LIVEKIT_MIN_RESOLUTION_SCALE must be greater than 0/),
|
||||
);
|
||||
await withHarnessEnv({LIVEKIT_ENABLE_SCREEN_AUDIO: 'maybe'}, () =>
|
||||
assert.throws(() => parseConfig(), /LIVEKIT_ENABLE_SCREEN_AUDIO must be a boolean flag/),
|
||||
);
|
||||
await withHarnessEnv({LIVEKIT_SCREEN_CODECS: 'vp8,h264', LIVEKIT_EXPECT_SCREEN_CODECS: 'vp8'}, () =>
|
||||
assert.throws(() => parseConfig(), /EXPECT_SCREEN_CODECS length must match/),
|
||||
);
|
||||
});
|
||||
|
||||
test('explicit secondary codec expectations override rotating defaults', async () => {
|
||||
await withHarnessEnv(
|
||||
{
|
||||
LIVEKIT_ENABLE_SECOND_PUBLISHER: '1',
|
||||
LIVEKIT_SCREEN_CODECS: 'vp8,h264',
|
||||
LIVEKIT_SECOND_PUBLISHER_CODEC: 'av1',
|
||||
LIVEKIT_EXPECT_SECOND_PUBLISHER_SCREEN_CODEC: 'hevc',
|
||||
},
|
||||
() => {
|
||||
const scenarios = buildScenarioConfigs(parseConfig());
|
||||
assert.deepEqual(
|
||||
scenarios.map((scenario) => scenario.secondaryPublisherCodec),
|
||||
['av1', 'av1'],
|
||||
);
|
||||
assert.deepEqual(
|
||||
scenarios.map((scenario) => scenario.expectedSecondaryPublisherCodec),
|
||||
['video/H265', 'video/H265'],
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('secondary screen codec aliases are accepted and conflicting aliases are rejected', async () => {
|
||||
await withHarnessEnv(
|
||||
{
|
||||
LIVEKIT_ENABLE_SECOND_PUBLISHER: '1',
|
||||
LIVEKIT_SCREEN_CODECS: 'h264',
|
||||
LIVEKIT_SECOND_PUBLISHER_SCREEN_CODECS: 'vp8',
|
||||
LIVEKIT_EXPECT_SECONDARY_PUBLISHER_SCREEN_CODEC: 'vp8',
|
||||
},
|
||||
() => {
|
||||
const config = parseConfig();
|
||||
assert.equal(config.secondaryPublisherCodec, 'vp8');
|
||||
assert.equal(config.expectedSecondaryPublisherCodec, 'video/VP8');
|
||||
},
|
||||
);
|
||||
|
||||
await withHarnessEnv(
|
||||
{
|
||||
LIVEKIT_ENABLE_SECOND_PUBLISHER: '1',
|
||||
LIVEKIT_SECOND_PUBLISHER_CODEC: 'h264',
|
||||
LIVEKIT_SECOND_PUBLISHER_SCREEN_CODEC: 'vp8',
|
||||
},
|
||||
() => assert.throws(() => parseConfig(), /conflicts with LIVEKIT_SECOND_PUBLISHER_CODEC/),
|
||||
);
|
||||
|
||||
await withHarnessEnv(
|
||||
{
|
||||
LIVEKIT_ENABLE_SECOND_PUBLISHER: '1',
|
||||
LIVEKIT_EXPECT_SECOND_PUBLISHER_SCREEN_CODEC: 'h264',
|
||||
LIVEKIT_EXPECT_SECONDARY_PUBLISHER_SCREEN_CODEC: 'vp8',
|
||||
},
|
||||
() => assert.throws(() => parseConfig(), /conflicts with LIVEKIT_EXPECT_SECOND_PUBLISHER_SCREEN_CODEC/),
|
||||
);
|
||||
});
|
||||
|
||||
test('external token setup derives identities, requires matching tokens, and redacts reports', async () => {
|
||||
await withHarnessEnv(
|
||||
{
|
||||
LIVEKIT_PUBLISHER_TOKEN: tokenForSubject('publisher-subject'),
|
||||
},
|
||||
() => assert.throws(() => parseConfig(), /both required/),
|
||||
);
|
||||
|
||||
await withHarnessEnv(
|
||||
{
|
||||
LIVEKIT_ROOM: 'shared-token-room',
|
||||
LIVEKIT_SCREEN_CODECS: 'vp8,h264',
|
||||
LIVEKIT_ENABLE_SECOND_PUBLISHER: '1',
|
||||
LIVEKIT_PUBLISHER_TOKEN: tokenForSubject('publisher-subject'),
|
||||
LIVEKIT_SUBSCRIBER_TOKEN: tokenForSubject('subscriber-subject'),
|
||||
},
|
||||
() => assert.throws(() => parseConfig(), /SECONDARY_PUBLISHER_TOKEN/),
|
||||
);
|
||||
|
||||
await withHarnessEnv(
|
||||
{
|
||||
LIVEKIT_ROOM: 'shared-token-room',
|
||||
LIVEKIT_SCREEN_CODECS: 'vp8,h264',
|
||||
LIVEKIT_ENABLE_SECOND_PUBLISHER: '1',
|
||||
LIVEKIT_PUBLISHER_TOKEN: tokenForSubject('publisher-subject'),
|
||||
LIVEKIT_SUBSCRIBER_TOKEN: tokenForSubject('subscriber-subject'),
|
||||
LIVEKIT_SECONDARY_PUBLISHER_TOKEN: tokenForSubject('secondary-subject'),
|
||||
LIVEKIT_E2EE_KEY: 'secret-key',
|
||||
},
|
||||
() => {
|
||||
const config = parseConfig();
|
||||
assert.equal(config.externalTokens, true);
|
||||
assert.equal(config.publisherIdentity, 'publisher-subject');
|
||||
assert.equal(config.subscriberIdentity, 'subscriber-subject');
|
||||
assert.equal(config.secondaryPublisherIdentity, 'secondary-subject');
|
||||
assert.deepEqual(
|
||||
buildScenarioConfigs(config).map((scenario) => scenario.room),
|
||||
['shared-token-room', 'shared-token-room'],
|
||||
);
|
||||
|
||||
const sanitized = sanitizeConfig(config);
|
||||
assert.equal(sanitized.url, 'ws://localhost:7880');
|
||||
assert.equal(sanitized.apiSecret, '<redacted>');
|
||||
assert.equal(sanitized.e2eeKey, '<present>');
|
||||
assert.equal(sanitized.publisherToken, '<present>');
|
||||
assert.equal(sanitized.subscriberToken, '<present>');
|
||||
assert.equal(sanitized.secondaryPublisherToken, '<present>');
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('report sanitization strips URL credentials, query strings, and fragments', async () => {
|
||||
await withHarnessEnv(
|
||||
{
|
||||
LIVEKIT_URL: 'wss://user:password@example.test:443/rtc?access_token=secret#fragment',
|
||||
},
|
||||
() => {
|
||||
const sanitized = sanitizeConfig(parseConfig());
|
||||
assert.equal(sanitized.url, 'wss://example.test/rtc');
|
||||
assert.doesNotMatch(JSON.stringify(sanitized), /user|password|access_token|secret|fragment/);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('jwt subject and TCP target parsing catch malformed live setups early', () => {
|
||||
assert.equal(jwtSubject(tokenForSubject('user_1_connection')), 'user_1_connection');
|
||||
assert.throws(() => jwtSubject('not-a-jwt'), /must be a JWT/);
|
||||
assert.throws(() => jwtSubject('header.bad-json.signature'), /failed to decode/);
|
||||
assert.throws(() => jwtSubject(tokenForSubject('')), /does not contain/);
|
||||
|
||||
assert.deepEqual(liveKitTcpTarget('ws://localhost:7880'), {host: 'localhost', port: 7880});
|
||||
assert.deepEqual(liveKitTcpTarget('wss://[::1]/rtc'), {host: '::1', port: 443});
|
||||
assert.equal(liveKitApiUrl('ws://localhost:7880/rtc?token=secret'), 'http://localhost:7880');
|
||||
assert.equal(liveKitApiUrl('wss://livekit.example.test/rtc'), 'https://livekit.example.test');
|
||||
assert.throws(() => liveKitTcpTarget('https://localhost'), /must use ws/);
|
||||
});
|
||||
|
||||
test('admin token has the room-scoped grant needed for server publishing validation', () => {
|
||||
const token = createLiveKitAdminToken({apiKey: 'devkey', apiSecret: 'secret', room: 'room-a'});
|
||||
const payload = jwtPayload(token);
|
||||
assert.equal(payload.iss, 'devkey');
|
||||
assert.equal(payload.video.room, 'room-a');
|
||||
assert.equal(payload.video.roomAdmin, true);
|
||||
assert.equal(payload.video.roomJoin, undefined);
|
||||
});
|
||||
|
||||
test('scenario room names are stable and bounded for generated-token suites', () => {
|
||||
assert.equal(scenarioRoomName('room', 'codec-vp8', 1, false), 'room');
|
||||
assert.equal(scenarioRoomName('room', 'codec:vP8 with H264', 2, false), 'room-codec-vP8-with-H264');
|
||||
assert.equal(scenarioRoomName('room', 'codec-vp8', 2, true), 'room');
|
||||
|
||||
const longRoom = scenarioRoomName('r'.repeat(120), 'codec-h264-with-hevc', 2, false);
|
||||
assert.equal(longRoom.length, 128);
|
||||
assert.ok(longRoom.startsWith('r'.repeat(120)));
|
||||
});
|
||||
|
||||
test('strict report checks fail when the strict window has too few samples to measure gaps', async () => {
|
||||
await withHarnessEnv(
|
||||
{
|
||||
LIVEKIT_MIN_RECEIVED_FPS_RATIO: '0',
|
||||
LIVEKIT_MAX_FRAME_GAP_MS: '250',
|
||||
LIVEKIT_MAX_AUDIO_FRAME_GAP_MS: '250',
|
||||
LIVEKIT_MAX_AV_DRIFT_MS: '0',
|
||||
},
|
||||
() => {
|
||||
const config = parseConfig();
|
||||
const report = strictReportFor(config, {
|
||||
videoFrameTimes: [1_250],
|
||||
audioFrameTimes: [1_300],
|
||||
});
|
||||
assert.equal(report.metrics.strictVideoFrames, 1);
|
||||
assert.equal(report.metrics.strictAudioFrames, 1);
|
||||
assert.match(strictFailures(report, config).join('\n'), /video frame gap unavailable/);
|
||||
assert.match(strictFailures(report, config).join('\n'), /audio frame gap unavailable/);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('strict report checks use only samples inside the measured window', async () => {
|
||||
await withHarnessEnv(
|
||||
{
|
||||
LIVEKIT_MIN_RECEIVED_FPS_RATIO: '0',
|
||||
LIVEKIT_MAX_FRAME_GAP_MS: '250',
|
||||
LIVEKIT_MAX_AUDIO_FRAME_GAP_MS: '250',
|
||||
LIVEKIT_MAX_AV_DRIFT_MS: '0',
|
||||
},
|
||||
() => {
|
||||
const config = parseConfig();
|
||||
const report = strictReportFor(config, {
|
||||
videoFrameTimes: [500, 1_050, 1_200, 2_500],
|
||||
audioFrameTimes: [600, 1_100, 1_240, 2_600],
|
||||
});
|
||||
assert.equal(report.metrics.strictVideoFrames, 2);
|
||||
assert.equal(report.metrics.strictAudioFrames, 2);
|
||||
assert.equal(report.metrics.maxVideoFrameGapMs, 150);
|
||||
assert.equal(report.metrics.maxAudioFrameGapMs, 140);
|
||||
assert.deepEqual(strictFailures(report, config), []);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('strict screen report ignores camera video callbacks for resolution and drift', async () => {
|
||||
await withHarnessEnv(
|
||||
{
|
||||
LIVEKIT_ENABLE_CAMERA: '1',
|
||||
LIVEKIT_MIN_RECEIVED_FPS_RATIO: '0',
|
||||
LIVEKIT_MAX_FRAME_GAP_MS: '250',
|
||||
LIVEKIT_MAX_AUDIO_FRAME_GAP_MS: '250',
|
||||
LIVEKIT_MAX_AV_DRIFT_MS: '80',
|
||||
},
|
||||
() => {
|
||||
const config = parseConfig();
|
||||
const report = strictReportFor(config, {
|
||||
videoFrameRecords: [
|
||||
{
|
||||
atMs: 1_010,
|
||||
identity: config.publisherIdentity,
|
||||
kind: 'video',
|
||||
source: 'screen_share',
|
||||
trackName: 'screen',
|
||||
trackSid: 'TR_screen',
|
||||
width: 320,
|
||||
height: 180,
|
||||
},
|
||||
{
|
||||
atMs: 1_025,
|
||||
identity: config.publisherIdentity,
|
||||
kind: 'video',
|
||||
source: 'camera',
|
||||
trackName: 'camera',
|
||||
trackSid: 'TR_camera',
|
||||
width: 480,
|
||||
height: 360,
|
||||
},
|
||||
{
|
||||
atMs: 1_080,
|
||||
identity: config.publisherIdentity,
|
||||
kind: 'video',
|
||||
source: 'screen_share',
|
||||
trackName: 'screen',
|
||||
trackSid: 'TR_screen',
|
||||
width: 320,
|
||||
height: 180,
|
||||
},
|
||||
{
|
||||
atMs: 1_220,
|
||||
identity: config.publisherIdentity,
|
||||
kind: 'video',
|
||||
source: 'camera',
|
||||
trackName: 'camera',
|
||||
trackSid: 'TR_camera',
|
||||
width: 480,
|
||||
height: 360,
|
||||
},
|
||||
],
|
||||
audioFrameTimes: [1_040, 1_100],
|
||||
});
|
||||
|
||||
assert.equal(report.metrics.videoCallbacks, 4);
|
||||
assert.equal(report.metrics.strictVideoFrames, 2);
|
||||
assert.deepEqual(report.metrics.videoResolutionCounts, {'320x180': 2});
|
||||
assert.equal(report.metrics.videoResolutionMismatchCount, 0);
|
||||
assert.equal(report.metrics.avDriftMs, 20);
|
||||
assert.deepEqual(strictFailures(report, config), []);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('strict packet loss gate uses loss deltas inside the strict window', async () => {
|
||||
await withHarnessEnv(
|
||||
{
|
||||
LIVEKIT_MIN_RECEIVED_FPS_RATIO: '0',
|
||||
LIVEKIT_MAX_FRAME_GAP_MS: '0',
|
||||
LIVEKIT_MAX_AUDIO_FRAME_GAP_MS: '0',
|
||||
LIVEKIT_MAX_AV_DRIFT_MS: '0',
|
||||
LIVEKIT_MAX_PACKET_LOSS: '0',
|
||||
},
|
||||
() => {
|
||||
const config = parseConfig();
|
||||
const report = strictReportFor(config, {
|
||||
videoFrameTimes: [1_100, 1_200],
|
||||
subscriberStatsSamples: [
|
||||
{atMs: 900, payload: {outbound: [], inbound: [{kind: 'video', packetsLost: 4}]}},
|
||||
{atMs: 1_500, payload: {outbound: [], inbound: [{kind: 'video', packetsLost: 4}]}},
|
||||
],
|
||||
});
|
||||
assert.equal(report.metrics.maxObservedPacketLoss, 4);
|
||||
assert.equal(report.metrics.maxObservedPacketLossDelta, 0);
|
||||
assert.deepEqual(strictFailures(report, config), []);
|
||||
|
||||
const failingReport = strictReportFor(config, {
|
||||
videoFrameTimes: [1_100, 1_200],
|
||||
subscriberStatsSamples: [
|
||||
{atMs: 900, payload: {outbound: [], inbound: [{kind: 'video', packetsLost: 4}]}},
|
||||
{atMs: 1_500, payload: {outbound: [], inbound: [{kind: 'video', packetsLost: 5}]}},
|
||||
],
|
||||
});
|
||||
assert.equal(failingReport.metrics.maxObservedPacketLossDelta, 1);
|
||||
assert.match(strictFailures(failingReport, config).join('\n'), /packet loss delta 1/);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('server publishing checks cover primary and secondary screen, audio, and camera tracks', async () => {
|
||||
await withHarnessEnv(
|
||||
{
|
||||
LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1',
|
||||
LIVEKIT_ENABLE_SECOND_PUBLISHER: '1',
|
||||
LIVEKIT_ENABLE_SCREEN_AUDIO: '1',
|
||||
LIVEKIT_ENABLE_CAMERA: '1',
|
||||
},
|
||||
() => {
|
||||
const config = parseConfig();
|
||||
const serverState = {
|
||||
participants: [
|
||||
{
|
||||
identity: config.publisherIdentity,
|
||||
tracks: [
|
||||
{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false, sid: 'TR_V1'},
|
||||
{type: 'AUDIO', source: 'MICROPHONE', mimeType: 'audio/red', muted: false, sid: 'TR_A1'},
|
||||
{type: 'AUDIO', source: 'SCREEN_SHARE_AUDIO', mimeType: 'audio/red', muted: false, sid: 'TR_A2'},
|
||||
{type: 'VIDEO', source: 'CAMERA', muted: false, sid: 'TR_V2'},
|
||||
],
|
||||
},
|
||||
{
|
||||
identity: config.secondaryPublisherIdentity,
|
||||
tracks: [
|
||||
{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false, sid: 'TR_V3'},
|
||||
{type: 'AUDIO', source: 'MICROPHONE', mimeType: 'audio/red', muted: false, sid: 'TR_A3'},
|
||||
{type: 'AUDIO', source: 'SCREEN_SHARE_AUDIO', mimeType: 'audio/red', muted: false, sid: 'TR_A4'},
|
||||
],
|
||||
},
|
||||
],
|
||||
error: null,
|
||||
};
|
||||
const checks = serverChecksFor(config, serverState);
|
||||
assert.equal(checks.length, 8);
|
||||
assert.deepEqual(
|
||||
checks.map((check) => [check.name, check.pass]),
|
||||
[
|
||||
['server publishing API participants listed', true],
|
||||
['server sees publisher screenshare publication', true],
|
||||
['server sees publisher microphone publication', true],
|
||||
['server sees publisher screen-share audio publication', true],
|
||||
['server sees publisher camera publication', true],
|
||||
['server sees secondary publisher screenshare publication', true],
|
||||
['server sees secondary publisher microphone publication', true],
|
||||
['server sees secondary publisher screen-share audio publication', true],
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('server publishing checks fail closed on missing server tracks', async () => {
|
||||
await withHarnessEnv({LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1', LIVEKIT_ENABLE_SCREEN_AUDIO: '1'}, () => {
|
||||
const config = parseConfig();
|
||||
const checks = serverChecksFor(config, {
|
||||
participants: [
|
||||
{
|
||||
identity: config.publisherIdentity,
|
||||
tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}],
|
||||
},
|
||||
],
|
||||
error: null,
|
||||
});
|
||||
assert.equal(findCheck(checks, 'server sees publisher screenshare publication').pass, true);
|
||||
assert.equal(findCheck(checks, 'server sees publisher screen-share audio publication').pass, false);
|
||||
});
|
||||
});
|
||||
|
||||
test('server publishing checks reject wrong participant identity', async () => {
|
||||
await withHarnessEnv({LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1'}, () => {
|
||||
const config = parseConfig();
|
||||
const checks = serverChecksFor(config, {
|
||||
participants: [
|
||||
{
|
||||
identity: config.subscriberIdentity,
|
||||
tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}],
|
||||
},
|
||||
],
|
||||
error: null,
|
||||
});
|
||||
assert.equal(findCheck(checks, 'server publishing API participants listed').pass, true);
|
||||
assert.equal(findCheck(checks, 'server sees publisher screenshare publication').pass, false);
|
||||
});
|
||||
});
|
||||
|
||||
test('server publishing checks reject wrong kind, source, and screen mime', async () => {
|
||||
await withHarnessEnv({LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1', LIVEKIT_EXPECT_SCREEN_CODEC: 'h264'}, () => {
|
||||
const config = parseConfig();
|
||||
const wrongKindChecks = serverChecksFor(config, {
|
||||
participants: [
|
||||
{
|
||||
identity: config.publisherIdentity,
|
||||
tracks: [{type: 'AUDIO', source: 'SCREEN_SHARE', mimeType: 'video/H264', muted: false}],
|
||||
},
|
||||
],
|
||||
error: null,
|
||||
});
|
||||
assert.equal(findCheck(wrongKindChecks, 'server sees publisher screenshare publication').pass, false);
|
||||
|
||||
const wrongSourceChecks = serverChecksFor(config, {
|
||||
participants: [
|
||||
{
|
||||
identity: config.publisherIdentity,
|
||||
tracks: [{type: 'VIDEO', source: 'CAMERA', mimeType: 'video/H264', muted: false}],
|
||||
},
|
||||
],
|
||||
error: null,
|
||||
});
|
||||
assert.equal(findCheck(wrongSourceChecks, 'server sees publisher screenshare publication').pass, false);
|
||||
|
||||
const wrongMimeChecks = serverChecksFor(config, {
|
||||
participants: [
|
||||
{
|
||||
identity: config.publisherIdentity,
|
||||
tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}],
|
||||
},
|
||||
],
|
||||
error: null,
|
||||
});
|
||||
assert.equal(findCheck(wrongMimeChecks, 'server sees publisher screenshare publication').pass, false);
|
||||
|
||||
const matchingChecks = serverChecksFor(config, {
|
||||
participants: [
|
||||
{
|
||||
identity: config.publisherIdentity,
|
||||
tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mime_type: 'video/h264', muted: false}],
|
||||
},
|
||||
],
|
||||
error: null,
|
||||
});
|
||||
assert.equal(findCheck(matchingChecks, 'server sees publisher screenshare publication').pass, true);
|
||||
});
|
||||
});
|
||||
|
||||
test('server publishing checks reject muted or missing mute state on expected tracks', async () => {
|
||||
await withHarnessEnv({LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1'}, () => {
|
||||
const config = parseConfig();
|
||||
const mutedChecks = serverChecksFor(config, {
|
||||
participants: [
|
||||
{
|
||||
identity: config.publisherIdentity,
|
||||
tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: true}],
|
||||
},
|
||||
],
|
||||
error: null,
|
||||
});
|
||||
assert.equal(findCheck(mutedChecks, 'server sees publisher screenshare publication').pass, false);
|
||||
|
||||
const missingMutedChecks = serverChecksFor(config, {
|
||||
participants: [
|
||||
{
|
||||
identity: config.publisherIdentity,
|
||||
tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8'}],
|
||||
},
|
||||
],
|
||||
error: null,
|
||||
});
|
||||
assert.equal(findCheck(missingMutedChecks, 'server sees publisher screenshare publication').pass, false);
|
||||
});
|
||||
});
|
||||
|
||||
test('server publishing checks handle protobuf numeric enums without accepting data tracks as video', async () => {
|
||||
await withHarnessEnv({LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1'}, () => {
|
||||
const config = parseConfig();
|
||||
const dataTrackChecks = serverChecksFor(config, {
|
||||
participants: [
|
||||
{
|
||||
identity: config.publisherIdentity,
|
||||
tracks: [{type: 2, source: 3, mimeType: 'video/VP8', muted: false}],
|
||||
},
|
||||
],
|
||||
error: null,
|
||||
});
|
||||
assert.equal(findCheck(dataTrackChecks, 'server sees publisher screenshare publication').pass, false);
|
||||
|
||||
const videoTrackChecks = serverChecksFor(config, {
|
||||
participants: [
|
||||
{
|
||||
identity: config.publisherIdentity,
|
||||
tracks: [{type: 1, source: 3, mimeType: 'video/VP8', muted: false}],
|
||||
},
|
||||
],
|
||||
error: null,
|
||||
});
|
||||
assert.equal(findCheck(videoTrackChecks, 'server sees publisher screenshare publication').pass, true);
|
||||
});
|
||||
});
|
||||
|
||||
test('server publishing checks fail closed on missing secondary, audio, camera, and screen-audio tracks', async () => {
|
||||
await withHarnessEnv(
|
||||
{
|
||||
LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1',
|
||||
LIVEKIT_ENABLE_SECOND_PUBLISHER: '1',
|
||||
LIVEKIT_ENABLE_SCREEN_AUDIO: '1',
|
||||
LIVEKIT_ENABLE_CAMERA: '1',
|
||||
},
|
||||
() => {
|
||||
const config = parseConfig();
|
||||
const checks = serverChecksFor(config, {
|
||||
participants: [
|
||||
{
|
||||
identity: config.publisherIdentity,
|
||||
tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}],
|
||||
},
|
||||
{
|
||||
identity: config.secondaryPublisherIdentity,
|
||||
tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}],
|
||||
},
|
||||
],
|
||||
error: null,
|
||||
});
|
||||
assert.equal(findCheck(checks, 'server sees publisher screenshare publication').pass, true);
|
||||
assert.equal(findCheck(checks, 'server sees publisher microphone publication').pass, false);
|
||||
assert.equal(findCheck(checks, 'server sees publisher screen-share audio publication').pass, false);
|
||||
assert.equal(findCheck(checks, 'server sees publisher camera publication').pass, false);
|
||||
assert.equal(findCheck(checks, 'server sees secondary publisher screenshare publication').pass, true);
|
||||
assert.equal(findCheck(checks, 'server sees secondary publisher microphone publication').pass, false);
|
||||
assert.equal(findCheck(checks, 'server sees secondary publisher screen-share audio publication').pass, false);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('strict server publishing validation does not pass open on Twirp errors', async () => {
|
||||
await withHarnessEnv({LIVEKIT_HARNESS_STRICT: '1'}, () => {
|
||||
const config = parseConfig();
|
||||
const checks = serverChecksFor(config, {
|
||||
participants: [
|
||||
{
|
||||
identity: config.publisherIdentity,
|
||||
tracks: [
|
||||
{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false},
|
||||
{type: 'AUDIO', source: 'MICROPHONE', mimeType: 'audio/red', muted: false},
|
||||
{type: 'AUDIO', source: 'SCREEN_SHARE_AUDIO', mimeType: 'audio/red', muted: false},
|
||||
],
|
||||
},
|
||||
{
|
||||
identity: config.secondaryPublisherIdentity,
|
||||
tracks: [
|
||||
{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false},
|
||||
{type: 'AUDIO', source: 'MICROPHONE', mimeType: 'audio/red', muted: false},
|
||||
{type: 'AUDIO', source: 'SCREEN_SHARE_AUDIO', mimeType: 'audio/red', muted: false},
|
||||
],
|
||||
},
|
||||
],
|
||||
error: 'RoomService.ListParticipants failed with HTTP 401',
|
||||
});
|
||||
assert.equal(config.validateServerPublishing, true);
|
||||
assert.equal(findCheck(checks, 'server publishing API participants listed').pass, false);
|
||||
assert.equal(
|
||||
checks.every((check) => check.pass),
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,92 +0,0 @@
|
||||
# Project-level configuration.
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(runner LANGUAGES CXX C)
|
||||
enable_language(ASM)
|
||||
|
||||
set(BINARY_NAME "h264_benchmark")
|
||||
|
||||
# Set the C++ standard to C++23.
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
|
||||
|
||||
add_definitions(-DWEBRTC_POSIX)
|
||||
add_definitions(-DWEBRTC_LINUX)
|
||||
add_definitions(-DUSE_UDEV)
|
||||
add_definitions(-DUSE_AURA=1)
|
||||
add_definitions(-DUSE_GLIB=1)
|
||||
add_definitions(-DUSE_OZONE=1)
|
||||
add_definitions(-D__STDC_CONSTANT_MACROS)
|
||||
add_definitions(-D__STDC_FORMAT_MACROS)
|
||||
add_definitions(-D_FILE_OFFSET_BITS=64)
|
||||
add_definitions(-D_LARGEFILE_SOURCE)
|
||||
add_definitions(-D_LARGEFILE64_SOURCE)
|
||||
add_definitions(-D_GNU_SOURCE)
|
||||
add_definitions(-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE)
|
||||
add_definitions(-D_GLIBCXX_ASSERTIONS=1)
|
||||
add_definitions(-DCR_CLANG_REVISION=\"llvmorg-19-init-8091-gab037c4f-1\")
|
||||
add_definitions(-DCR_SYSROOT_KEY=20230611T210420Z-2)
|
||||
add_definitions(-DDYNAMIC_ANNOTATIONS_ENABLED=1)
|
||||
add_definitions(-DWEBRTC_ENABLE_PROTOBUF=0)
|
||||
add_definitions(-DWEBRTC_STRICT_FIELD_TRIALS=0)
|
||||
add_definitions(-DWEBRTC_INCLUDE_INTERNAL_AUDIO_DEVICE)
|
||||
add_definitions(-DRTC_USE_LIBAOM_AV1_ENCODER)
|
||||
add_definitions(-DRTC_ENABLE_VP9)
|
||||
add_definitions(-DRTC_DAV1D_IN_INTERNAL_DECODER_FACTORY)
|
||||
add_definitions(-DWEBRTC_HAVE_SCTP)
|
||||
add_definitions(-DWEBRTC_USE_H264)
|
||||
add_definitions(-DWEBRTC_ENABLE_LIBEVENT)
|
||||
add_definitions(-DWEBRTC_LIBRARY_IMPL)
|
||||
add_definitions(-DWEBRTC_ENABLE_AVX2)
|
||||
|
||||
include_directories(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../libwebrtc/linux-x64-debug/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../libwebrtc/linux-x64-release/include/third_party/abseil-cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../libwebrtc/linux-x64-release/include/third_party/libyuv/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../src/nvidia/NvCodec/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../src/nvidia/NvCodec/NvCodec"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
|
||||
)
|
||||
|
||||
link_libraries(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../libwebrtc/linux-x64-release/lib/libwebrtc.a"
|
||||
)
|
||||
|
||||
find_package (Threads)
|
||||
|
||||
add_executable(${BINARY_NAME}
|
||||
"test_main.cc"
|
||||
"benchmark.cc"
|
||||
"benchmark_openh264.cc"
|
||||
"video_source.cc"
|
||||
"fileutils.cc"
|
||||
"cpu/cpu_linux.cc"
|
||||
|
||||
"benchmark_nvidia.cc"
|
||||
"../src/nvidia/NvCodec/NvCodec/NvDecoder/NvDecoder.cpp"
|
||||
"../src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoder.cpp"
|
||||
"../src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoderCuda.cpp"
|
||||
"../src/nvidia/h264_encoder_impl.cpp"
|
||||
"../src/nvidia/h264_decoder_impl.cpp"
|
||||
"../src/nvidia/nvidia_decoder_factory.cpp"
|
||||
"../src/nvidia/nvidia_encoder_factory.cpp"
|
||||
"../src/nvidia/cuda_context.cpp"
|
||||
"../src/nvidia/implib/libcuda.so.init.c"
|
||||
"../src/nvidia/implib/libcuda.so.tramp.S"
|
||||
"../src/nvidia/implib/libnvcuvid.so.init.c"
|
||||
"../src/nvidia/implib/libnvcuvid.so.tramp.S"
|
||||
|
||||
"benchmark_vaapi.cc"
|
||||
"../src/vaapi/vaapi_display_drm.cpp"
|
||||
"../src/vaapi/vaapi_h264_encoder_wrapper.cpp"
|
||||
"../src/vaapi/vaapi_encoder_factory.cpp"
|
||||
"../src/vaapi/h264_encoder_impl.cpp"
|
||||
"../src/vaapi/implib/libva-drm.so.init.c"
|
||||
"../src/vaapi/implib/libva-drm.so.tramp.S"
|
||||
"../src/vaapi/implib/libva.so.init.c"
|
||||
"../src/vaapi/implib/libva.so.tramp.S"
|
||||
)
|
||||
|
||||
target_link_libraries(${BINARY_NAME} ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(${BINARY_NAME} dl)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user