fix(wzp-video): gate shiguredo AV1 crates to macOS only; fix Linux relay build

- Cargo.toml: merge duplicate [target.macos.deps] sections; move
  shiguredo_dav1d/svt_av1/video_toolbox into single block
- lib.rs: dav1d + svt_av1 modules and re-exports guarded by
  cfg(target_os = "macos") instead of cfg(not(android))
- factory.rs: AV1 encoder/decoder paths split into macos (svt-av1/dav1d)
  and linux fallback (NotInitialized); update doc comments and tests
- build-linux-docker.sh: build only wzp-relay + wzp-web (drops
  wzp-client which pulled in shiguredo crates); fix Docker copy step;
  add --deploy flag + deploy_relay(); fix branch auto-detection
- build-tauri-android.sh: default to release build, arm64 only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-05-25 06:33:35 +04:00
parent 4ac62d99e0
commit 5d05b021aa
5 changed files with 39 additions and 41 deletions

View File

@@ -10,13 +10,12 @@ bytes = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
wzp-proto = { path = "../wzp-proto" } wzp-proto = { path = "../wzp-proto" }
# AV1 SW codecs do not support Android target (build.rs panics on # AV1 SW codecs: shiguredo crates download prebuilt binaries at build time.
# aarch64-linux-android). Android uses MediaCodec for AV1 instead. # Prebuilts are available for macOS only; Android uses MediaCodec; Linux will
[target.'cfg(not(target_os = "android"))'.dependencies] # use system/vendored libs when that path is wired up (TODO).
[target.'cfg(target_os = "macos")'.dependencies]
shiguredo_dav1d = "2026.1.0" shiguredo_dav1d = "2026.1.0"
shiguredo_svt_av1 = "2026.1.0" shiguredo_svt_av1 = "2026.1.0"
[target.'cfg(target_os = "macos")'.dependencies]
shiguredo_video_toolbox = "2026.1" shiguredo_video_toolbox = "2026.1"
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(target_os = "android")'.dependencies]

View File

@@ -11,7 +11,7 @@ use crate::encoder::{VideoEncoder, VideoError};
/// **Encoder dispatch:** /// **Encoder dispatch:**
/// - `H264Baseline` → `VideoToolboxEncoder` (macOS) / `MediaCodecEncoder` (Android) /// - `H264Baseline` → `VideoToolboxEncoder` (macOS) / `MediaCodecEncoder` (Android)
/// - `H265Main` → `VideoToolboxHevcEncoder` (macOS) / `MediaCodecHevcEncoder` (Android) /// - `H265Main` → `VideoToolboxHevcEncoder` (macOS) / `MediaCodecHevcEncoder` (Android)
/// - `Av1Main` → `SvtAv1Encoder` (all platforms — universal SW fallback) /// - `Av1Main` → `SvtAv1Encoder` (macOS only — SW fallback)
/// ///
/// Non-video codecs return [`VideoError::InvalidInput`]. /// Non-video codecs return [`VideoError::InvalidInput`].
pub fn create_video_encoder( pub fn create_video_encoder(
@@ -78,10 +78,15 @@ pub fn create_video_encoder(
#[allow(clippy::needless_return)] #[allow(clippy::needless_return)]
return Err(VideoError::NotInitialized); return Err(VideoError::NotInitialized);
} }
#[cfg(not(target_os = "android"))] #[cfg(target_os = "macos")]
{ {
Ok(Box::new(crate::svt_av1::SvtAv1Encoder::new(width, height)?)) Ok(Box::new(crate::svt_av1::SvtAv1Encoder::new(width, height)?))
} }
#[cfg(not(any(target_os = "macos", target_os = "android")))]
{
let _ = (width, height);
Err(VideoError::NotInitialized)
}
} }
_ => Err(VideoError::InvalidInput("not a video codec".into())), _ => Err(VideoError::InvalidInput("not a video codec".into())),
} }
@@ -92,7 +97,7 @@ pub fn create_video_encoder(
/// **Decoder dispatch:** /// **Decoder dispatch:**
/// - `H264Baseline` → `VideoToolboxDecoder` (macOS) / `MediaCodecDecoder` (Android) /// - `H264Baseline` → `VideoToolboxDecoder` (macOS) / `MediaCodecDecoder` (Android)
/// - `H265Main` → `VideoToolboxHevcDecoder` (macOS) / `MediaCodecHevcDecoder` (Android) /// - `H265Main` → `VideoToolboxHevcDecoder` (macOS) / `MediaCodecHevcDecoder` (Android)
/// - `Av1Main` → `VideoToolboxAv1Decoder` (macOS M3+) → `Dav1dDecoder` (fallback, all platforms) /// - `Av1Main` → `VideoToolboxAv1Decoder` (macOS M3+) → `Dav1dDecoder` (macOS SW fallback)
/// ///
/// Non-video codecs return [`VideoError::InvalidInput`]. /// Non-video codecs return [`VideoError::InvalidInput`].
pub fn create_video_decoder( pub fn create_video_decoder(
@@ -154,10 +159,15 @@ pub fn create_video_decoder(
return crate::mediacodec::MediaCodecAv1Decoder::new(width, height) return crate::mediacodec::MediaCodecAv1Decoder::new(width, height)
.map(|d| Box::new(d) as Box<dyn VideoDecoder>); .map(|d| Box::new(d) as Box<dyn VideoDecoder>);
} }
#[cfg(not(target_os = "android"))] #[cfg(target_os = "macos")]
{ {
Ok(Box::new(crate::dav1d::Dav1dDecoder::new()?)) Ok(Box::new(crate::dav1d::Dav1dDecoder::new()?))
} }
#[cfg(not(any(target_os = "macos", target_os = "android")))]
{
let _ = (width, height);
Err(VideoError::NotInitialized)
}
} }
_ => Err(VideoError::InvalidInput("not a video codec".into())), _ => Err(VideoError::InvalidInput("not a video codec".into())),
} }
@@ -170,30 +180,24 @@ mod tests {
#[test] #[test]
fn av1_encoder_factory_creates_svt_av1() { fn av1_encoder_factory_creates_svt_av1() {
let enc = create_video_encoder(CodecId::Av1Main, 640, 480, 2_000_000); let enc = create_video_encoder(CodecId::Av1Main, 640, 480, 2_000_000);
#[cfg(target_os = "android")] #[cfg(target_os = "macos")]
assert!(enc.is_ok(), "AV1 encoder factory should succeed on macOS");
#[cfg(not(target_os = "macos"))]
assert!( assert!(
matches!(enc, Err(VideoError::NotInitialized)), matches!(enc, Err(VideoError::NotInitialized)),
"AV1 SW encoder is unavailable on Android (no shiguredo_svt_av1)" "AV1 SW encoder is unavailable on Android/Linux (no shiguredo_svt_av1)"
);
#[cfg(not(target_os = "android"))]
assert!(
enc.is_ok(),
"AV1 encoder factory should succeed on non-Android platforms"
); );
} }
#[test] #[test]
fn av1_decoder_factory_creates_decoder() { fn av1_decoder_factory_creates_decoder() {
let dec = create_video_decoder(CodecId::Av1Main, 640, 480); let dec = create_video_decoder(CodecId::Av1Main, 640, 480);
#[cfg(target_os = "android")] #[cfg(target_os = "macos")]
assert!(dec.is_ok(), "AV1 decoder factory should succeed on macOS (dav1d fallback)");
#[cfg(not(target_os = "macos"))]
assert!( assert!(
matches!(dec, Err(VideoError::NotInitialized)), matches!(dec, Err(VideoError::NotInitialized)),
"AV1 decoder requires MediaCodec on Android; non-Android device returns NotInitialized" "AV1 decoder unavailable on Android/Linux (no shiguredo_dav1d)"
);
#[cfg(not(target_os = "android"))]
assert!(
dec.is_ok(),
"AV1 decoder factory should succeed on non-Android (dav1d SW fallback)"
); );
} }

View File

@@ -6,7 +6,7 @@
pub mod av1_obu; pub mod av1_obu;
pub mod controller; pub mod controller;
#[cfg(not(target_os = "android"))] #[cfg(target_os = "macos")]
pub mod dav1d; pub mod dav1d;
pub mod decoder; pub mod decoder;
pub mod depacketizer; pub mod depacketizer;
@@ -17,13 +17,13 @@ pub mod framer;
pub mod mediacodec; pub mod mediacodec;
pub mod nack; pub mod nack;
pub mod simulcast; pub mod simulcast;
#[cfg(not(target_os = "android"))] #[cfg(target_os = "macos")]
pub mod svt_av1; pub mod svt_av1;
pub mod videotoolbox; pub mod videotoolbox;
pub use av1_obu::{Av1Depacketizer, Av1ObuFramer, is_keyframe_obu}; pub use av1_obu::{Av1Depacketizer, Av1ObuFramer, is_keyframe_obu};
pub use controller::{VideoQualityController, VideoTarget}; pub use controller::{VideoQualityController, VideoTarget};
#[cfg(not(target_os = "android"))] #[cfg(target_os = "macos")]
pub use dav1d::Dav1dDecoder; pub use dav1d::Dav1dDecoder;
pub use decoder::VideoDecoder; pub use decoder::VideoDecoder;
pub use depacketizer::H264Depacketizer; pub use depacketizer::H264Depacketizer;
@@ -37,7 +37,7 @@ pub use mediacodec::{
}; };
pub use nack::{CachedPacket, NackAction, NackReceiver, NackSender}; pub use nack::{CachedPacket, NackAction, NackReceiver, NackSender};
pub use simulcast::{LayerPacket, LayerTarget, SimulcastEncoder, SimulcastLayer}; pub use simulcast::{LayerPacket, LayerTarget, SimulcastEncoder, SimulcastLayer};
#[cfg(not(target_os = "android"))] #[cfg(target_os = "macos")]
pub use svt_av1::SvtAv1Encoder; pub use svt_av1::SvtAv1Encoder;
pub use videotoolbox::{ pub use videotoolbox::{
VideoToolboxAv1Decoder, VideoToolboxDecoder, VideoToolboxEncoder, VideoToolboxHevcDecoder, VideoToolboxAv1Decoder, VideoToolboxDecoder, VideoToolboxEncoder, VideoToolboxHevcDecoder,

View File

@@ -105,20 +105,15 @@ docker run --rm --user 1000:1000 \
set -euo pipefail set -euo pipefail
cd /build/source cd /build/source
echo ">>> Building relay + client + web + bench..." echo ">>> Building relay + web..."
cargo build --release --bin wzp-relay --bin wzp-client --bin wzp-web --bin wzp-bench 2>&1 | tail -5 cargo build --release --bin wzp-relay --bin wzp-web 2>&1 | tail -5
echo ">>> Building audio client..."
cargo build --release --bin wzp-client --features audio 2>&1 | tail -3
cp target/release/wzp-client target/release/wzp-client-audio
cargo build --release --bin wzp-client 2>&1 | tail -3
echo ">>> Binaries:" echo ">>> Binaries:"
ls -lh target/release/wzp-relay target/release/wzp-client target/release/wzp-client-audio target/release/wzp-web target/release/wzp-bench ls -lh target/release/wzp-relay target/release/wzp-web
echo ">>> Packaging..." echo ">>> Packaging..."
tar czf /tmp/wzp-linux-x86_64.tar.gz \ tar czf /tmp/wzp-linux-x86_64.tar.gz \
-C target/release wzp-relay wzp-client wzp-client-audio wzp-web wzp-bench -C target/release wzp-relay wzp-web
echo "BINARIES_BUILT" echo "BINARIES_BUILT"
' '
@@ -131,7 +126,7 @@ TARBALL="$BASE_DIR/data/cache-linux/target/release/../../../wzp-linux-x86_64.tar
docker run --rm \ docker run --rm \
-v "$BASE_DIR/data/cache-linux/target:/build/target" \ -v "$BASE_DIR/data/cache-linux/target:/build/target" \
wzp-android-builder bash -c \ wzp-android-builder bash -c \
"cp /build/target/release/wzp-relay /build/target/release/wzp-client /build/target/release/wzp-client-audio /build/target/release/wzp-web /build/target/release/wzp-bench /tmp/ && tar czf /tmp/wzp-linux-x86_64.tar.gz -C /tmp wzp-relay wzp-client wzp-client-audio wzp-web wzp-bench && cat /tmp/wzp-linux-x86_64.tar.gz" \ "cp /build/target/release/wzp-relay /build/target/release/wzp-web /tmp/ && tar czf /tmp/wzp-linux-x86_64.tar.gz -C /tmp wzp-relay wzp-web && cat /tmp/wzp-linux-x86_64.tar.gz" \
> /tmp/wzp-linux-x86_64.tar.gz > /tmp/wzp-linux-x86_64.tar.gz
URL=$(curl -s -F "file=@/tmp/wzp-linux-x86_64.tar.gz" -H "Authorization: $rusty_auth_token" "$rusty_address") URL=$(curl -s -F "file=@/tmp/wzp-linux-x86_64.tar.gz" -H "Authorization: $rusty_auth_token" "$rusty_address")

View File

@@ -15,8 +15,8 @@ set -euo pipefail
# - Output: desktop/src-tauri/gen/android/.../*.apk # - Output: desktop/src-tauri/gen/android/.../*.apk
# #
# Usage: # Usage:
# ./scripts/build-tauri-android.sh # full pipeline (debug, arm64 only) # ./scripts/build-tauri-android.sh # full pipeline (release, arm64 only)
# ./scripts/build-tauri-android.sh --release # release APK # ./scripts/build-tauri-android.sh --debug # debug APK (faster, no optimisation)
# ./scripts/build-tauri-android.sh --no-pull # skip git fetch # ./scripts/build-tauri-android.sh --no-pull # skip git fetch
# ./scripts/build-tauri-android.sh --rust # force-clean rust target # ./scripts/build-tauri-android.sh --rust # force-clean rust target
# ./scripts/build-tauri-android.sh --init # also run `cargo tauri android init` # ./scripts/build-tauri-android.sh --init # also run `cargo tauri android init`
@@ -38,7 +38,7 @@ SSH_OPTS="-o ConnectTimeout=15 -o ServerAliveInterval=15 -o ServerAliveCountMax=
REBUILD_RUST=0 REBUILD_RUST=0
DO_PULL=1 DO_PULL=1
DO_INIT=0 DO_INIT=0
BUILD_RELEASE=0 BUILD_RELEASE=1
BUILD_ARCH="arm64" BUILD_ARCH="arm64"
NEXT_IS_ARCH=0 NEXT_IS_ARCH=0
for arg in "$@"; do for arg in "$@"; do
@@ -52,7 +52,7 @@ for arg in "$@"; do
--pull) DO_PULL=1 ;; --pull) DO_PULL=1 ;;
--no-pull) DO_PULL=0 ;; --no-pull) DO_PULL=0 ;;
--init) DO_INIT=1 ;; --init) DO_INIT=1 ;;
--release) BUILD_RELEASE=1 ;; --debug) BUILD_RELEASE=0 ;;
--arch) NEXT_IS_ARCH=1 ;; --arch) NEXT_IS_ARCH=1 ;;
-h|--help) -h|--help)
sed -n '3,32p' "$0" sed -n '3,32p' "$0"