Skip to content

feat: migrate video playback from AVPro to UUAV - #9463

Open
NickKhalow wants to merge 25 commits into
devfrom
feat/migrate-avpro-to-uuav
Open

feat: migrate video playback from AVPro to UUAV#9463
NickKhalow wants to merge 25 commits into
devfrom
feat/migrate-avpro-to-uuav

Conversation

@NickKhalow

@NickKhalow NickKhalow commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Pull Request Description

What does this PR change?

Migrates video/audio media streaming from the AVPro Video Ultra package to UUAV - an FFmpeg-based player with a native Rust core, embedded directly in the repo under Assets/Plugins/UUAV. Supported platforms: Windows x86_64 (D3D11) and macOS arm64 (Metal); Linux stays compiled out, as before.

  • Removes com.renderheads.avpro.video-ultra from Packages/manifest.json - UUAV ships in-tree, no external package reference.

Plugin layout (Assets/Plugins/UUAV)

native/ - the Rust crate that builds the native player (uuav.dll / libuuav.dylib): FFmpeg 8 demux/decode, hardware-accelerated video decoding, audio pulled through OnAudioFilterRead. Platform-specific code lives in _windows/_macos sibling modules (hw_device_*, video_decoder_*, video_output_*) selected with #[cfg(target_os)] at the dispatch site - no cross-platform stub contracts.

  • Windows: D3D11VA hardware decode; one NV12 D3D11 resource shared by both planes. FFmpeg comes from prebuilt BtbN LGPL shared DLLs (avcodec-63 et al.).
  • macOS: VideoToolbox decode → CVPixelBuffer/IOSurface → two Metal plane textures (Y R8Unorm, UV RG8Unorm) blitted on the plugin's own MTLCommandQueue. FFmpeg (n8.1) is built from source via native/scripts/build-ffmpeg-macos.sh into a git-ignored .third_party/ (source-build sonames differ from the Windows DLLs: avcodec 62 etc. - build.sh resolves majors dynamically). build.sh also handles @rpath dylib naming and the mandatory ad-hoc code signing on arm64.
  • build.sh is cross-platform: it detects the host OS, builds the right cargo target, and deploys binaries into Packages/UUAV/Runtime/Plugins/{x86_64,macOS}.

Packages/UUAV/Runtime - the C# binding layer (NativeMethods, UUAVRuntime, UUAVPlayer, UUAVClient.asmdef) plus the prebuilt binaries. On both platforms the native side presents NV12 planes; Unity wraps them with Texture2D.CreateExternalTexture and converts to RGB with the bundled Hidden/UUAV/NV12ToRGB shader (Graphics.Blit), with frame presentation driven by GL.IssuePluginEvent. On macOS, UUAVRuntime refuses to initialize on any graphics API other than Metal. Local-file playback is editor-only; player builds accept streaming URLs only.

Packages/UUAV/AVProCompat - a RenderHeads.Media.AVProVideo.MediaPlayer facade over UUAVPlayer (MediaPlayer, MediaPlayerEvent, Enums, Interfaces, UUAVBackend), so the existing MediaStream code (systems, pool, tests) keeps compiling against the AVPro API surface largely unchanged.

Packages/UUAV/Example - a minimal standalone example scene/assembly for the player, independent of Explorer.

Explorer integration

  • Adapts MediaStream to the compat surface: drops the AutoOpen flag in MediaPlayerCustomPool and the manual SetAudioSource wiring in MultiMediaPlayer (the compat MediaPlayer handles audio itself); updates MediaPlayer.prefab accordingly.
  • Links UUAV.AVProCompat into the ECS.Unity, DCL.Plugins, and DCL.EditMode.Tests asmdefs; adds injection/skip logs to MediaPlayerPluginWrapper.
  • Retargets the AV_PRO_PRESENT version-define in DCL.Plugins from com.renderheads.avpro.video-ultra to com.nickkhalow.uuav (ProjectSettings scripting defines are unchanged vs dev).

Test Instructions

Media streaming now runs on two switchable backends: AVPro (default) and UUAV. The backend is picked once at startup from the use-custom-media-player feature flag, and can be forced locally with the --use-custom-media-player launch argument (--use-custom-media-player false forces AVPro even if the flag is on). The startup log prints which backend is active: Media player backend: AVPro or Media player backend: UUAV.

Steps (default backend - AVPro):

metaforge explorer run 9463

Expected result:
Startup log prints Media player backend: AVPro. Video streams in scenes play back correctly (e.g. video screens in Genesis Plaza or any scene using VideoPlayer/media streams), with audio in sync - identical to dev.

Steps (UUAV backend - launch argument):
Install the build via metaforge explorer run 9463, then launch the installed Explorer executable directly with the launch argument (metaforge does not pass args through):

# Windows
Decentraland.exe --use-custom-media-player
# macOS
open Decentraland.app --args --use-custom-media-player

(or enable the use-custom-media-player feature flag)

Expected result:
Startup log prints Media player backend: UUAV. Media behavior is indistinguishable from the AVPro run - the flag must not change SDK-observable behavior.

Steps (fresh account):

metaforge account create --clear
metaforge explorer run 9463

Expected result:
Same as above - media streams play with no errors in logs.

Prerequisites

  • Windows x86_64 build, or
  • macOS arm64 (Apple Silicon) build - requires Metal (UUAV backend)

Test Steps

Run once per backend (AVPro default, then with --use-custom-media-player):

  1. Enter a scene that uses video streaming (video screens / PBVideoPlayer).
  2. Verify the video plays, pauses, and seeks as driven by the scene.
  3. Verify a scene-driven stop resets the video to the start (not just a pause) - this must behave the same on both backends.
  4. Verify audio plays and stays in sync with video.
  5. Roam between scenes to exercise media player pooling (create/release).
  6. Repeat on both platforms - for UUAV, the Windows D3D11 path and the macOS Metal path are separate native implementations.

Additional Testing Notes

  • AVPro stays in the manifest and remains the default; UUAV is opt-in via the flag, so an absent or failed feature-flag fetch keeps the battle-tested player.
  • The media player systems are always compiled in except on Linux, where they are excluded by platform defines (the AV_PRO_PRESENT gate is gone) - watch for the [MediaPlayerPluginWrapper] Inject vs Ignore Inject log on startup.
  • With the UUAV backend active, watch logs for native library load errors on startup (uuav.dll/libuuav.dylib, FFmpeg libraries).
  • On macOS, UUAV refuses to initialize on any graphics API other than Metal (logs an error by design).
  • The AVProCompat layer intentionally covers only the AVPro features MediaStream consumes - it is poll-based and will need extending as new AVPro API surface is used.

Quality Checklist

  • Changes have been tested locally
  • Documentation has been updated (if required)
  • Performance impact has been considered
  • For SDK features: Test scene is included

Code Review Reference

Please review our Branch & PR Standards before submitting. It explains the automated review flow, QA/DEV approval requirements, and what each label does - especially useful for first-time contributors.

@NickKhalow NickKhalow added the force-build Used to trigger a build on draft PR label Jul 22, 2026
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

@NickKhalow NickKhalow self-assigned this Jul 22, 2026
@NickKhalow NickKhalow added Windows Only Issue only happens on Windows builds force-build Used to trigger a build on draft PR and removed force-build Used to trigger a build on draft PR Windows Only Issue only happens on Windows builds labels Jul 22, 2026
@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

badge

Warnings count reduced: 14054 => 13981

Warnings/errors in files changed by this PR (42)
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:140  ArrangeRedundantParentheses  Redundant parentheses
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:141  ArrangeRedundantParentheses  Redundant parentheses
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:142  ArrangeRedundantParentheses  Redundant parentheses
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:222  ArrangeRedundantParentheses  Redundant parentheses
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:223  ArrangeRedundantParentheses  Redundant parentheses
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:224  ArrangeRedundantParentheses  Redundant parentheses
Assets/DCL/FeatureFlags/FeatureFlagsStrings.cs:184  CSharpWarnings::CS0618  CS0618: Constant 'DCL.FeatureFlags.FeatureFlagsStrings.GPUI_ENABLED' is obsolete: 'GPU Instancer Pro terrain is no longer optional so the flag is not needed'
Assets/DCL/SDKComponents/MediaStream/GoogleDriveMediaExtensions.cs:25  CSharpWarnings::CS8603  Possible null reference return
Assets/DCL/SDKComponents/MediaStream/GoogleDriveMediaExtensions.cs:40  CSharpWarnings::CS8603  Possible null reference return
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:27  CSharpWarnings::CS8618  Non-nullable field 'api' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:31  CSharpWarnings::CS8618  Non-nullable field 'engine' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:30  CSharpWarnings::CS8618  Non-nullable field 'jsOperations' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:29  CSharpWarnings::CS8618  Non-nullable field 'messagePipe' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:357  CSharpWarnings::CS8618  Non-nullable field 'onSceneMessage' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:28  CSharpWarnings::CS8618  Non-nullable field 'sceneCommunicationPipe' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:366  CSharpWarnings::CS8625  Cannot convert null literal to non-nullable reference type
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:366  CSharpWarnings::CS8767  Nullability of type of parameter 'specialRecipient' does not match implicitly implemented member 'void CrdtEcsBridge.JsModulesImplementation.Communications.ISceneCommunicationPipe.SendMessage(ReadOnlySpan<byte>, string, ConnectivityAssertiveness, CancellationToken, string?)' (possibly because of nullability attributes)
Assets/DCL/SDKComponents/MediaStream/GoogleDriveMediaExtensions.cs:25  ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract  Expression is always true according to nullable reference types' annotations
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:25  InconsistentNaming  Name 'CommunicationControllerAPIImplementationShould' does not match rule 'members_should_be_pascal_case'. Suggested name is 'CommunicationControllerApiImplementationShould'.
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:359  ParameterHidesMember  Parameter 'onSceneMessage' hides field 'CrdtEcsBridge.JsModulesImplementation.Tests.CommunicationControllerAPIImplementationShould.TestSceneCommunicationPipe.onSceneMessage'
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:364  ParameterHidesMember  Parameter 'onSceneMessage' hides field 'CrdtEcsBridge.JsModulesImplementation.Tests.CommunicationControllerAPIImplementationShould.TestSceneCommunicationPipe.onSceneMessage'
Assets/Plugins/UUAV/Packages/UUAV/Editor/HelperBuildPostprocessor.cs:84  PossibleNullReferenceException  Possible 'System.NullReferenceException'
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:177  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:257  RedundantAssignment  Value assigned is not used in any execution path
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:179  RedundantExplicitArrayCreation  Redundant explicit array type specification
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:271  RedundantExplicitArrayCreation  Redundant explicit array type specification
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:20  RedundantUsingDirective  Using directive is not required by the code and can be safely removed
Assets/DCL/SDKComponents/MediaStream/Systems/MediaPlayerPluginWrapper.cs:5  RedundantUsingDirective  Using directive is not required by the code and can be safely removed
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:140  UselessBinaryOperation  Addition or subtraction of 0 in every execution path, which is useless
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:141  UselessBinaryOperation  Addition or subtraction of 0 in every execution path, which is useless
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:142  UselessBinaryOperation  Addition or subtraction of 0 in every execution path, which is useless
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:150  UselessBinaryOperation  Addition or subtraction of 0 in every execution path, which is useless
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:160  UselessBinaryOperation  Addition or subtraction of 0 in every execution path, which is useless
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:170  UselessBinaryOperation  Addition or subtraction of 0 in every execution path, which is useless
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:199  UselessBinaryOperation  Addition or subtraction of 0 in every execution path, which is useless
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:222  UselessBinaryOperation  Addition or subtraction of 0 in every execution path, which is useless
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:223  UselessBinaryOperation  Addition or subtraction of 0 in every execution path, which is useless
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:224  UselessBinaryOperation  Addition or subtraction of 0 in every execution path, which is useless
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:230  UselessBinaryOperation  Addition or subtraction of 0 in every execution path, which is useless
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:240  UselessBinaryOperation  Addition or subtraction of 0 in every execution path, which is useless
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:250  UselessBinaryOperation  Addition or subtraction of 0 in every execution path, which is useless
Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs:291  UselessBinaryOperation  Addition or subtraction of 0 in every execution path, which is useless

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

badge

All Unity tests passed ✅

TESTS SUITE Result Passed Failed Skipped
EditMode ✅ Passed 24492 0 13
PlayMode ✅ Passed 236 0 5

@NickKhalow
NickKhalow marked this pull request as ready for review July 24, 2026 11:21
@NickKhalow
NickKhalow requested review from a team as code owners July 24, 2026 11:21
@github-actions
github-actions Bot requested review from DafGreco, dalkia and popuz July 24, 2026 11:21
@decentraland-bot
decentraland-bot self-requested a review July 24, 2026 11:21
@claude

This comment has been minimized.

@decentraland-bot decentraland-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review: feat: migrate video playback from AVPro to UUAV

STEP 2 — Root-cause check

This PR replaces a commercial video playback dependency (AVPro Video Ultra) with an embedded FFmpeg-based Rust/C# plugin (UUAV). This is a deliberate component swap, not a bug fix. The migration strategy — providing an AVProCompat facade in the same RenderHeads.Media.AVProVideo namespace so existing consumers compile unchanged — is a sound approach that minimizes integration risk.

Verdict: PASS — the change addresses a dependency swap at its root.

STEP 3 — Design & integration

Architecture: The UUAV plugin is structured in three layers:

  1. Native (Rust) — FFmpeg 8 demux/decode, D3D11VA/Metal HW accel, audio ring buffer. Ships as prebuilt uuav.dll/libuuav.dylib via LFS.
  2. Runtime (C#)UUAVPlayer (MonoBehaviour), UUAVRuntime (static lifecycle), NativeMethods (P/Invoke). Standalone, no DCL dependency.
  3. AVProCompat (C#) — Thin facade (MediaPlayer, IMediaControl, ITextureProducer, etc.) in RenderHeads.Media.AVProVideo namespace so DCL's MediaStream systems compile without changes.

Owner search: The MediaStream lifecycle is owned by MediaPlayerPluginWrapper (injection), MediaPlayerCustomPool (creation/pooling), and per-scene ECS systems (CreateMediaPlayerSystem, UpdateMediaPlayerSystem, CleanUpMediaPlayerSystem). The UUAV plugin does NOT duplicate these lifecycle owners — it replaces the underlying engine behind the same facade. The existing create/pool/release/cleanup paths remain unchanged.

Extension method compatibility verified: MediaPlayerExtensions.cs defines CloseCurrentStream, CrossfadeVolume, UpdatePlayback, UpdatePlaybackProperties as extension methods on MediaPlayer. All call only properties/methods that exist on the compat MediaPlayer (Stop(), CloseMedia(), Events, AudioVolume, MediaOpened, Control). Confirmed compatible.

Security review: Protocol whitelist (UNTRUSTED_STREAMING_PROTOCOLS) correctly restricts untrusted scene URLs to streaming protocols (https,http,tls,tcp,...). file: protocol is only added in UNITY_EDITOR builds. No credentials, secrets, or hardcoded tokens found. FFI boundary uses proper marshaling and string cleanup via uuav_string_free. No security issues identified.

STEP 5 — Line-level findings

See inline comments below for all findings with suggestion blocks.

Summary of findings:

# Severity File Issue
1 P1 DCL.Plugins.asmdef AV_PRO_PRESENT define likely not activated — package not registered with UPM
2 P2 UUAVBackend.cs Stop() only pauses, does not seek to 0 (diverges from AVPro semantics)
3 P2 MediaPlayer.cs Same Stop() issue on the compat MonoBehaviour
4 P2 UUAVPlayer.cs #region AI generated with TODO "recheck carefully" should not ship
5 P2 UUAVPlayer.cs Redundant P/Invoke uuav_player_state() calls in Update() — cache once
6 P2 package.json TODO placeholder values in description and keywords
7 P2 Enums.cs Typos in comment: "Effectivly" → "Effectively", "compatabilitty" → "compatibility"

STEP 6 — Complexity assessment

COMPLEX — Introduces an embedded native Rust/FFmpeg plugin with C# FFI bindings (P/Invoke, render-thread callbacks via GL.IssuePluginEvent, audio-thread callbacks via OnAudioFilterRead), an AVPro API compatibility layer, and modifies assembly definitions, package references, and the media playback pipeline.

STEP 7 — QA assessment

QA_REQUIRED: YES — This changes the entire video/audio playback engine. User-visible media playback (video screens, audio streams in scenes) must be tested on Windows. Audio sync, seeking, looping, pooling (scene transitions), and error handling all need manual verification.

STEP 8 — Non-blocking warnings

No warnings (Main scene not modified).

STEP 9 — Verdict

REVIEW_RESULT: FAIL ❌
COMPLEXITY: COMPLEX
COMPLEXITY_REASON: Introduces an embedded native Rust/FFmpeg plugin with C# P/Invoke bindings, AVPro API compat layer, audio/render thread callbacks, and modifies assembly definitions and package references across the media playback pipeline.
QA_REQUIRED: YES


Reviewed by Jarvis 🤖 · Requested by decentraland-bot via GitHub

Comment thread Explorer/Assets/DCL/PluginSystem/DCL.Plugins.asmdef Outdated
Comment thread Explorer/Assets/Plugins/UUAV/Packages/UUAV/AVProCompat/UUAVBackend.cs Outdated
Comment thread Explorer/Assets/Plugins/UUAV/Packages/UUAV/AVProCompat/MediaPlayer.cs Outdated
Comment thread Explorer/Assets/Plugins/UUAV/Packages/UUAV/Runtime/UUAVPlayer.cs Outdated
Comment thread Explorer/Assets/Plugins/UUAV/Packages/UUAV/Runtime/UUAVPlayer.cs
Comment thread Explorer/Assets/Plugins/UUAV/Packages/UUAV/package.json Outdated
Comment thread Explorer/Assets/Plugins/UUAV/Packages/UUAV/AVProCompat/Enums.cs
@claude

claude Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Security-Focused Dependency Review — PR #9463 (AVPro → UUAV) · re-run on synchronize

  • Re-inventory dependency & binary changes (PR updated since last review)
  • Re-check manifest / asmdef / AV_PRO_PRESENT gating
  • Re-check native binaries: LFS consistency, provenance, integrity
  • Re-check C# binding & compat layer (protocol whitelist, interop)
  • Classify risk & write verdict

View job run

@claude

claude Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Claude Code is working…

I'll analyze this and get back to you.

View job run

@claude

This comment has been minimized.

@claude

This comment has been minimized.

@claude

This comment has been minimized.

@dalkia dalkia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth considering setting UUAV as another video provider which lives along AVPro for some time? Given that the same API is shared, using RENum show allow it

@claude

This comment has been minimized.

@decentraland-bot decentraland-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review: feat: migrate video playback from AVPro to UUAV

STEP 2 — Root-cause check

This is a feature migration, not a bug fix. The PR replaces the AVPro Video Ultra commercial package with UUAV — a custom FFmpeg-based player with a native Rust core. The diff directly addresses the stated goal: it introduces the UUAV plugin, adds a backend-switching abstraction layer (DCL.AvProSwitch), wires it into the composition root via a use-custom-media-player feature flag, and replaces all RenderHeads.Media.AVProVideo references with DCL.AvProSwitch.

PASS — the diff addresses the migration goal directly.


STEP 3 — Design & integration

Architecture summary. Three layers:

  1. UUAV Plugin (Assets/Plugins/UUAV/) — standalone reusable package: native Rust core (NativeMethods, UUAVRuntime, UUAVPlayer) plus an AVPro-compatible facade (UUAV.Compat).
  2. AvProSwitch (Assets/DCL/AvProSwitch/) — Explorer-specific abstraction: MediaPlayerBackend REnum tagged union dispatching to UuavBackend or AvProBackend, MediaPlayerBackendSelection static global driven by feature flag, and a MediaPlayer MonoBehaviour that picks the backend in Awake().
  3. Explorer integration — namespace swaps from RenderHeads.Media.AVProVideo to DCL.AvProSwitch, assembly reference updates, prefab update.

Owner search — MediaPlayerBackendSelection: This static global holds the backend choice. It is installed in MediaPlayerContainer.InitializeInternalAsync() (the container that owns the media player composition root) before any player is created, and reset via [RuntimeInitializeOnLoadMethod] for domain reload safety. This correctly lives outside the pool/player lifecycle — it's a one-time configuration value, not a per-entity lifecycle. No existing owner can host this — justified.

Owner search — DCL.AvProSwitch.MediaPlayer: This MonoBehaviour is the prefab component that owns the backend union. It replaces the old RenderHeads.Media.AVProVideo.MediaPlayer component on the MediaPlayer.prefab. The pool (MediaPlayerCustomPool) creates and manages these instances — that lifecycle is unchanged. Existing owner (pool + prefab) hosts this — correct placement.

Teardown trace:

  • UUAVPlayer.OnDestroy() — calls uuav_player_free(playerId), releases plane textures, destroys runtimeSurface and nv12Material. ✅
  • UUAVRuntime.Deinit() — called on Application.quitting and on beforeAssemblyReload in editor. Calls uuav_deinit() and resets renderCallback. ✅
  • UUAVPlayer.OnDisable() — pauses if playing. ✅
  • Compat.MediaPlayer — does not own any additional subscriptions; Update() calls backend.Tick() which is stateless. ✅
  • DCL.AvProSwitch.MediaPlayer — no subscriptions to tear down; backends are Plain Old Classes, not IDisposable. ✅

Note on the three-layer facade stack: The path UUAV → Compat.MediaPlayer → AvProSwitch.UuavBackend → MediaPlayerBackend → AvProSwitch.MediaPlayer is deep. The Compat layer exists for reusability (the UUAV plugin is designed to work outside Explorer), and the AvProSwitch layer provides a neutral abstraction independent of both backends. This is defensible for the transition period but adds significant forwarding boilerplate. Consider collapsing the Compat layer into UuavBackend if the UUAV plugin doesn't need to be consumed by other projects.

PASS — design is sound, lifecycles are correctly managed.


STEP 4 — Member audit

MediaPlayer.Control, .Info, .TextureProducer — all return MediaPlayerBackend (the full tagged union). In the old API these returned different interfaces (IMediaControl, IMediaInfo, ITextureProducer). This widens the surface per accessor, but consumers only call the relevant subset of methods. This is an acceptable trade-off for the REnum dispatch approach — no issue.

MediaPlayer.AudioSource — forwards to backend.AudioSource. Used by MultiMediaPlayer (pool audio setup). Single forwarding accessor that centralizes access to an owned object — legitimate encapsulation, not a merge candidate.

MediaPlayerBackendSelection.UseCustomPlayer — consumed by MediaPlayer.Awake() and logged by MediaPlayerContainer. Two consumers, both legitimate reads of the same config value.

MediaPlayer.HasControl, .IsReady — forward to backend.HasControl / backend.IsReady. These replace direct null-checks against player.Control != null / player.TextureProducer != null (see MultiMediaPlayer.cs diff). The new accessors encapsulate the null-check semantics correctly — UuavBackend always returns true (surfaces exist for the component's lifetime), AvProBackend delegates to AVPro's lazy initialization. Good refactor.


STEP 5 — Line-level review

P1 — Lint regression (CI blocker)

The lint check reports 14,159 compilation warnings vs. a baseline of 14,094 — 65 new warnings, failing the Enforce warning reduction (PR) step. These likely originate from the UUAV plugin code under Assets/Plugins/UUAV/ (nullable warnings from csc.rsp -nullable:enable, REnum source-generated code for MediaPlayerBackend.g.cs, etc.).

Fix: Add an .editorconfig or a Directory.Build.props under Assets/Plugins/UUAV/ to suppress warnings in the plugin directory, or add targeted #pragma warning disable directives to the UUAV files that generate the most noise. The REnum-generated MediaPlayerBackend.g.cs warnings (CS8625) may also need a #nullable disable in the AvProSwitch csc.rsp or a pragma in the source.

P2 — FeatureId naming convention violation

FeatureId.USE_CUSTOM_MEDIA_PLAYER uses SCREAMING_SNAKE_CASE. All other 70 members of this enum use PascalCase (McpServer, CreditsTopup, ByteWeightedLoadingProgress, etc.). Rename to UseCustomMediaPlayer for consistency. This also applies to the dictionary entry in FeaturesRegistry and the reference in MediaPlayerContainer.cs.


Security review

  • FFmpeg protocol whitelist (UUAVRuntime.cs): Production builds restrict protocols to https,http,tls,tcp,crypto,data,udp,rtp,rtcp,rtsp. file: is only added in UNITY_EDITOR. This correctly prevents local file access from untrusted scene URLs. ✅
  • Native binaries: Prebuilt uuav.dll / libuuav.dylib and FFmpeg shared libraries are committed as binary blobs. Trust depends on the build pipeline integrity. The new-dependency label is correctly applied, and the Dependency Security Review CI check is pending. ✅
  • No secrets or credentials in the diff.
  • No injection surfaces — all string inputs go through the native FFI boundary with proper marshalling. ✅

No security findings.


CI Status

Check Status Notes
Test (playmode) ✅ PASS
Test (editmode) ❌ FAIL Appears to be a CI infrastructure issue (Unity licensing error in logs), not caused by this PR
Lint ❌ FAIL 65 new warnings (14,159 vs. 14,094 baseline) — must be addressed
Build (windows64) ⏳ PENDING
Build (macos) ⏳ PENDING
Dependency Security Review ⏳ PENDING MEDIUM RISK — human review required for new native binaries
Semantic title ✅ PASS feat: prefix is correct

STEP 6 — Complexity

COMPLEX — introduces new native dependencies, modifies assembly definitions, changes plugin registration/container wiring, touches the media playback subsystem, adds feature flag integration.

STEP 7 — QA assessment

QA_REQUIRED: YES — changes runtime video/audio playback behavior (the core media player backend), affects what users see and hear in-world.

STEP 8 — Non-blocking warnings

No Main.unity scene changes detected.

STEP 9 — Verdict

REVIEW_RESULT: FAIL ❌
COMPLEXITY: COMPLEX
COMPLEXITY_REASON: Introduces native UUAV plugin with FFI bindings, adds AvProSwitch backend abstraction with REnum dispatch, modifies assembly definitions and plugin registration, adds feature flag integration for runtime backend selection
QA_REQUIRED: YES


Reviewed by Jarvis 🤖 · Requested by Juan Ignacio Molteni [Dalkia] (<@U03JSUQ5Z7U>) via Slack

Comment thread Explorer/Assets/DCL/FeatureFlags/FeaturesRegistry.cs Outdated
Comment thread Explorer/Assets/DCL/FeatureFlags/FeaturesRegistry.cs Outdated
Comment thread Explorer/Assets/DCL/PluginSystem/csc.rsp Outdated
@claude

This comment has been minimized.

@dalkia dalkia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Im reviewing the C# side:

Architecture note + a couple of general questions

UUAV path has two redundant wrapper layers behind the switch

The REnum switch itself is a good fit for engine selection. My concern is the UUAV side of it, which stacks two independently-designed abstraction layers that each invented a "backend."

When an AvProSwitch.MediaPlayer wakes with the flag on, the two Awakes chain into each other and the GameObject ends up with three media components and two separate backend fields:

AvProSwitch.MediaPlayer.Awake()
  → new DCL.AvProSwitch.UuavBackend(go)              // backend #1: "which engine"
       → go.AddComponent<UUAV.Compat.MediaPlayer>()
            → Compat.MediaPlayer.Awake()
                 → go.AddComponent<UUAVPlayer>()
                 → new UUAV.Compat.UUAVBackend(player) // backend #2: "adapt to AVPro shape"

So DCL.AvProSwitch.UuavBackend and UUAV.Compat.MediaPlayer are both near-pure forwarding layers — the only real logic (UUAVState → play/pause/buffering, Stop = pause+seek0, seek-settling, readiness gate) lives in a third class, UUAV.Compat.UUAVBackend. Compare the AVPro side: AvProBackend wraps RenderHeads' MediaPlayer in a single hop. UUAV takes three, for the same result.

The root cause is that UUAV.Compat (inside the vendored plugin) was built as a standalone AVPro drop-in — its own MediaPlayer + backend + IMediaControl/Info/TextureProducer — and then AvProSwitch wraps that facade again. Two "backend"s in a trench coat.

Suggestion — collapse the UUAV column to one hop, matching AVPro:

  • Merge DCL.AvProSwitch.UuavBackend + UUAV.Compat.MediaPlayer into a single Uuav adapter that attaches UUAVPlayer directly (in its constructor, like AvProBackend does) and absorbs Compat.UUAVBackend's state-mapping. Delete the Compat IMediaControl/Info/TextureProducer interfaces and the duplicated Enums/TimeRanges — that layer only exists to mimic AVPro's shape.
  • The per-frame Tick() (seek-settling) that currently forces Compat.MediaPlayer to be a MonoBehaviour has a natural home: AvProSwitch.MediaPlayer is already a MonoBehaviour and already owns the backend, so add Update() => backend.Tick(); there, with Tick() as a Match arm on the union (UUAV settles the seek, AVPro is a no-op since RenderHeads' component self-ticks). No ECS involvement, and it runs under the same active/enabled condition as today since it's the same GameObject.

I'm not suggesting we flatten AVPro + UUAV into MultiMediaPlayer as peer REnum cases — they're the same kind of player (same ops, which is exactly why the compat facade works), so that would just duplicate every Match arm. The real capability split is file-video vs LiveKit, which MultiMediaPlayer already models. Keep the engine switch, just make it one clean layer.

General questions

Rename classes/namespaces so the stack is engine-agnostic Right now the AVPro name leaks all the way up even though the whole point is that either engine can run: the namespace is AvProSwitch, the MultiMediaPlayer case is AvProPlayer, and its field is AvProMediaPlayer. That privileges AVPro in the naming and makes MultiMediaPlayer read as if it's tied to one engine. I'd rename the switch and its members engine-neutral — e.g. type/namespace MediaBackend, cases Uuav/AvPro — so nothing above the switch has to know which player is running.

The next two are nice-to-haves, not blocking this PR — just worth keeping in mind:

  1. Are we still committed to supporting Intel (x86_64) macs? UUAV ships native binaries via P/Invoke, so every supported platform is another native build target to produce and maintain — Intel mac support in particular is worth confirming before we lock in the build matrix.

  2. Worth moving UUAV to its own repo and consuming prebuilt binaries via GitHub Actions? Right now the native libs are vendored into this repo, which means a maintainer has to build for every platform locally to iterate. Splitting UUAV into a standalone repo (e.g. https://github.com/NickKhalow/uuav) with CI building the per-platform binaries would make iteration much easier and keep the native artifacts out of the Unity repo. Just something to keep in mind, and it pairs naturally with the platform-matrix question above.

@claude

This comment has been minimized.

@NickKhalow
NickKhalow force-pushed the feat/migrate-avpro-to-uuav branch from ca2e106 to 45425f7 Compare July 29, 2026 14:34
@claude

This comment has been minimized.

@claude

This comment has been minimized.

@claude

This comment has been minimized.

@claude

This comment has been minimized.

@claude

claude Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Claude finished @NickKhalow's task in 5m 50s —— View job


Security-Focused Dependency Review — PR #9463 (AVPro → UUAV) · re-run on synchronize

  • Diff the new commits against the last-reviewed state (22457f0ec)
  • Re-verify H1 (helpers not in LFS)
  • Re-verify H2 (IPC peer authentication) and H4 (CloseHandle primitive)
  • Inventory the changed native binaries (FFmpeg soname shift, libzmq)
  • Re-check manifest / lockfile / asmdefs / defines / protocol whitelist
  • Classify risk & write verdict

What landed this round

Two commits (6d538aa4e, badfa7f6f), +108 / −105 across 39 files, all Windows dependency hygiene. No C# and no Rust logic changed. STEP W does not apply — nothing under .github/.

  • 6d538aa4e update dlls — the Windows FFmpeg set was swapped wholesale: avcodec-63 / avformat-63 / avutil-61 / swresample-7 out, avcodec-62 / avformat-62 / avutil-60 / swresample-6 in; avdevice-63, avfilter-12, swscale-10 deleted. The five MSYS2 support DLLs (libbz2-1, libiconv-2, liblzma-5, libwinpthread-1, zlib1) deleted. libzmq.dll and uuav.dll rebuilt. uuav-helper.exe converted to an LFS pointer.
  • badfa7f6f update build pipeline[profile.release] strip = "symbols"; build.sh hard-fails when .third_party/ffmpeg/bin is absent and deploys the four runtime DLLs from the same tree it just linked; build-libzmq.sh links gcc/stdc++/winpthread statically, strips, and sets CMAKE_POLICY_VERSION_MINIMUM=3.5; README rewritten with a complete per-platform shipping inventory.

Two findings close. One reopens with a new failure mode.


✅ M9 — resolved (cleanest fix of the round)

The seven-DLL unscripted MSYS2 drop is gone entirely, and the diff explains why rather than just deleting:

  • build-libzmq.sh:92-DCMAKE_SHARED_LINKER_FLAGS="-static-libstdc++ -static-libgcc -static -lpthread", so libzmq.dll resolves without MSYS2 on PATH.
  • README.md:111 — "the helper's import closure is exactly the four DLLs above (NV12-only pipeline), the BtbN shared build links its support libs statically, and both Rust binaries and the libzmq build are self-contained."

zlib / bz2 / xz — the three with the long CVE history — no longer ship as separately-versioned artifacts at all. That's better than hashing them.

✅ New — FFmpeg is now ABI-aligned across platforms and tied to a release

Windows moved off a post-8.0 master snapshot (avcodec 63 / avutil 61 / swresample 7) onto the same 8.1 majors the macOS source build produces (avcodec 62 / avformat 62 / avutil 60 / swresample 6). README.md:110 ties them to ffmpeg-sys-next = "8.1.0", and build.sh:66-73 now refuses to run without .third_party/ffmpeg/bin and copies the runtime DLLs out of the exact tree the helper linked against — the deploy-from-a-different-build drift is structurally closed, not just documented. Dropping avfilter / avdevice / swscale also removes three parsers' worth of Windows attack surface.


🔴 HIGH — H1: half fixed on Windows, and the half that moved is now a build break

The Windows helper's blob is an LFS pointer now:

Runtime/Plugins/x86_64/uuav-helper.exe   132 B pointer → oid aacb3cf9…, size 1,422,336   ✓
Runtime/Plugins/macOS/uuav-helper        2,094,816 B raw Mach-O universal                 ✗

But no committed .gitattributes rule matches it. I read both files in the tree:

  • .gitattributes:16-17*.dll, *.dylib. No *.exe, no path rule for uuav-helper.
  • Explorer/.gitattributes* text=auto plus two linguist-vendored overrides. Nothing else.

So the pointer was written with the LFS clean filter active locally while the tracking rule stayed out of the commit. Git LFS decides what to smudge from the filter attribute, and nothing sets it for this path — on a fresh clone what lands on disk is the 132-byte pointer text named uuav-helper.exe. From there:

  1. HelperBuildPostprocessor.CopyWindowsHelper (HelperBuildPostprocessor.cs:39-46) copies it into <Game>_Data/Plugins/x86_64/.
  2. spawn.rs:14-31 Command::new(path).spawn() fails — it's a text file, not a PE.
  3. The recovery worker then retries it three times and parks in Failed.

This is H3's build failure again one layer down, and it's the specific reason the rule has to be committed rather than applied locally. Both helpers still need path-based rules plus a git lfs migrate for the macOS blob, which remains the one UUAV binary with no LFS-OID integrity trail.

Explorer/Assets/Plugins/UUAV/Packages/UUAV/Runtime/Plugins/**/uuav-helper filter=lfs diff=lfs merge=lfs -text
Explorer/Assets/Plugins/UUAV/Packages/UUAV/Runtime/Plugins/**/uuav-helper.exe filter=lfs diff=lfs merge=lfs -text

I'm reading the repo, not a Windows build log — if the Windows job is green, the runner is being fed a smudged copy some other way and that's worth understanding before merge, because a contributor's clone won't be. Fix this →


🔴 HIGH — H4: unchanged, byte-identical

Re-verified this round so it isn't read as stale. On Windows, ToClient::TextureSet carries handles: Vec<u64> of raw NT handle values owned by the client from receipt, and the no-mirror path honors that ownership unconditionally:

// registry.rs:236-254
#[cfg(target_os = "windows")]
pub fn apply_texture_set(registry: &Registry, id: PlayerId,, handles: Vec<u64>) {
    if let Some(mirror) = registry.by_helper(id)
        && let Ok(mut video) = mirror.video.lock()
    { video.store_texture_set(generation, width, height, handles); return; }
    // no live mirror to own the duplicated handles: close them here or they
    // leak in Unity's process (the player was freed mid-announcement)
    crate::present::close_handles(&handles);   // ← unconditional CloseHandle loop
}

close_handles (present_windows.rs:204-214) is a bare for &value in handles { CloseHandle(as_handle(value)) }. Reaching it needs no token, no valid player id, no handshake win — just one frame with an id that misses by_helper. The ensure!(pending.handles.len() == SLOTS) guard at open_generation (present_windows.rs:220-224) still runs on the render thread after acceptance and the no-mirror path never reaches it, and there is still no set_maxmsgsize anywhere in the workspace (grepped), so the Vec length is unbounded. Closing live handles inside a running process is the setup half of a handle-recycling type confusion.

🔴 HIGH — H2: unchanged, byte-identical

  • socket.rs:37-41 — Windows endpoint is tcp://127.0.0.1:*; the bound DEALER accepts every local peer. build-libzmq.sh still -DENABLE_CURVE=OFF, so no ZAP/CURVE is available.
  • connection.rs:112-120 — token and ABI validated on the first frame only.
  • connection.rs route() — no per-peer credential on any arm; ToClient::Hello { .. } => { /* handshake is over; ignore */ }.
  • spawn.rs:17-23--token <uuid> still in argv, readable from the process table.

Consequences unchanged: unauthenticated init DoS, AudioPacket → the audio ring, Log/PlayerError → Unity's log sinks, State/MediaInfo → the getter mirrors, Reply{corr} → satisfies a pending request(), DEALER round-robin leaking roughly half of ToServer (including OpenMedia { url }). macOS remains fine — ipc://$TMPDIR/uuav-<uuid>.sock is a UDS in the per-user private dir.

Smallest sufficient fix is still: token on every frame, dropped in route(), and off the command line — plus ZMQ_MAXMSGSIZE and a handles length cap, which closes H4 with it. Fix this →


🟡 MEDIUM

M1 — narrowed, still open. libzmq is sha256-pinned (ZMQ_SHA256 + shasum -c) and now self-contained. FFmpeg is not: macOS clones tag n8.1 (a mutable ref), and Windows is a manual download of a BtbN release asset with no hash — README.md:59 documents the expected majors precisely, and build.sh:68-72 now guards presence, but nothing checks content. A sha256 manifest checked in build.sh alongside the existing directory guard is the obvious extension of the pattern build-libzmq.sh already demonstrates.

M6 — helper still spawns even when UUAV is off. UUAVRuntime.cs:46-47 unchanged: [RuntimeInitializeOnLoadMethod(BeforeSceneLoad)] private static void Init(), unconditional and private. With the Windows path live that's a child process and a loopback TCP listener on every launch, before MediaPlayerBackendSelection.Install has read the flag, with AVPro as the default backend. Make Init public and call it from InitializeInternalAsync only when UseCustomPlayer is true — that also fixes the Linux DllNotFoundException (UUAVClient.asmdef "includePlatforms": []).

M7 — no ZMQ_MAXMSGSIZE. socket::dealer (socket.rs:20-26) sets only HWM and linger — confirmed by grep, maxmsgsize appears nowhere in native/. Two unbounded Vecs come off the wire from an unauthenticated peer: AudioPacket { samples: Vec<f32> } and TextureSet { handles: Vec<u64> }.

M10 — the Windows helper is still a console-subsystem executable. No windows_subsystem attribute anywhere in native/, and no creation_flags call — spawn.rs:16 is a bare Command::new(&path). A GUI Unity player spawning a console child allocates a visible console window, and the recovery worker respawns it, so a flapping helper flashes windows. #![windows_subsystem = "windows"] on uuav-server or .creation_flags(CREATE_NO_WINDOW).

M3 (postprocessor gating)OnPostprocessBuild (HelperBuildPostprocessor.cs:26-37) is still ungated by the feature flag, deploying the helper into every standalone build while AVPro is the default backend.

M8 — macOS bundle signing vs. the helper copy, unchanged (HelperBuildPostprocessor.cs:49-54).

M5 — macOS mach service in the shared bootstrap namespace: bootstrap_register("uuav.<token>") with the argv-visible token, Receiver::recv still doesn't check msgh_id.

M4 — FFmpeg protocol_whitelist unchanged and still client-authored (UUAVRuntime.cs:15). The list itself is right — https,http,tls,tcp,crypto,data,udp,rtp,rtcp,rtsp, no file: outside UNITY_EDITOR, no concat/subfile pivots — the note is that the helper accepts whatever the client sends rather than enforcing its own floor.


🔵 Minor — LFS payload weight

strip = "symbols" measurably helped the Rust artifacts: uuav.dll 1,972,132 → 1,345,024, uuav-helper.exe 2,107,425 → 1,422,336, libzmq.dll −40%. But the FFmpeg DLLs dominate and are unstripped:

Windows (LFS size) macOS (LFS size, universal, 2 slices)
avcodec 70,875,136 29,020,960 (≈14.5 MB/slice)
avformat 22,070,784
avutil 2,937,856
swresample 723,968

Windows avcodec-62.dll is ~5× the per-slice size of the macOS source build of the same release — the BtbN drop is carrying debug sections. The Windows set is ~96 MB and every rebuild adds a fresh copy to LFS permanently. stripping the deployed FFmpeg copies in build.sh (as build-libzmq.sh:100 already does for libzmq) would cut the single largest contributor. Not a security issue.

Other minor, all unchanged:

  • .cargo/config.toml:15-16 still hardcodes linker = "C:\\msys64\\mingw64\\bin\\gcc.exe" under # TODO why exactly this linker?
  • build.sh's *) catch-all still sends any non-Darwin host down the Windows branch, Linux included — though it now fails fast on the missing .third_party/ffmpeg guard instead of silently producing a broken deploy
  • macOS still ships libavdevice.62 / libavfilter.11 / libswscale.9, the three Windows just dropped as never-imported. If the import closure is genuinely NV12-only on both platforms, the macOS set can shrink the same way

Dependency inventory — delta this round

Dependency / file Version Source Type Scope Risk Confidence
uuav-helper.exe (1,422,336 B) crate 0.2.0 vendored Rust (uuav-server) native executable, LFS pointer with no matching .gitattributes rule runtime, spawned unconditionally HIGH (H1) HIGH
uuav-helper (2,094,816 B, universal) crate 0.2.0 vendored Rust native executable, raw blob, NOT LFS runtime HIGH (H1) HIGH
uuav.dll (rebuilt, stripped) crate 0.2.0 vendored Rust (uuav-client) native binary, LFS 81d868f8… Windows runtime MEDIUM HIGH
libzmq.dll (rebuilt, static runtimes, stripped) 4.3.5, sha256-pinned build-libzmq.sh native binary, LFS 7a890500… Windows runtime LOW HIGH
avcodec-62 / avformat-62 / avutil-60 / swresample-6 FFmpeg 8.1 (LGPL shared) BtbN release, manual download, unhashed native binary, LFS Windows runtime MEDIUM (M1) HIGH
libwinpthread-1, libiconv-2, libbz2-1, liblzma-5, zlib1 removed ✅ M9 closed HIGH
avdevice-63, avfilter-12, swscale-10 removed (never imported) ✅ surface reduced HIGH
macOS FFmpeg dylibs (7) n8.1 source build build-ffmpeg-macos.sh, tag-only native binary, LFS macOS runtime MEDIUM (M1) HIGH

manifest.json:25 still carries com.renderheads.avpro.video-ultra at git@github.com:decentraland/unity-explorer-packages.git?path=/AVProVideo — a git URL with no ref or tag, so it floats. That predates this PR and isn't introduced by it, but it's the shipping default backend and worth pinning separately. packages-lock.json untouched. AVPro remaining the default is still the reason none of this is worse than it is.


Verdict

Good round on the dependency surface. M9 is genuinely closed — not hashed but deleted, which is better — and the Windows FFmpeg drop moved off a master snapshot onto the same 8.1 majors macOS builds, with build.sh now structurally preventing the deploy-vs-link mismatch. That's the right direction on M1 and M2 both.

But H2/H4 are byte-identical to two rounds ago: registry.rs:253 will still CloseHandle a list of attacker-chosen integers inside Unity's own process on a single unauthenticated frame — no token, no valid player, no length bound. That's the blocker, and it needs the design decision, not a patch.

H1 got worse in a new way. Converting uuav-helper.exe to LFS without committing a .gitattributes rule leaves the tree in a state where clones get pointer text where a PE should be. That's H3's build failure re-entering through the front door, and the macOS helper is still a raw blob.

DEPENDENCY_REVIEW: BLOCK
· feat/migrate-avpro-to-uuav

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

force-build Used to trigger a build on draft PR new-dependency

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants