promotional bannermobile promotional banner

WATERMeDIA: Multimedia API

Library with an API using VLC for multimedia integration with Minecraft
Back to Files
File namewatermedia-3.0.0.22.jar
Uploader
SrRapero720SrRapero720
Uploaded
Jul 24, 2026
Downloads
209
Size
3.4 MB
Mod Loaders
ForgeFabricNeoForge
File ID
8498341
Type
B
Beta
Supported game versions
  • 26.2
  • 26.1.2
  • 1.21.11
  • 1.21.1
  • 1.20.1
  • 1.19.2
  • 1.18.2

Curse Maven Snippet

Forge

implementation "curse.maven:watermedia-869524:8498341"

Fabric

modImplementation "curse.maven:watermedia-869524:8498341"

NeoForge

implementation "curse.maven:watermedia-869524:8498341"

Learn more about Curse Maven

What's new

📦 UPDATE 3.0.0.22 (BETA)

⚡ Core — Boot pipeline, modules & progress metrics

  • ⚙️ Renamed: WaterMediaAPIWaterMediaModule (moved to org.watermedia) — non-API boot processes extend it too now; the lifecycle (load/start/release) is protected, so only the WaterMedia orchestrator (same package) can drive it
  • ⚙️ Changed: Binaries and Config are homologated as boot modules — WaterMediaBinaries and WaterMediaConfig extend WaterMediaModule and boot through the same registry, ordered Binaries → Config → Codecs → Platform → Media → Network; the hand-rolled pre-registry startup blocks in WaterMedia.start are gone
  • ⚙️ Changed: the global load() pre-pass is interleaved per module (loadstart), so config-dependent step counts (e.g. NetworkAPI's file-server step) read registered config values instead of compile-time defaults
  • ⚙️ Changed: the FFmpeg bootstrap is centralized in MediaAPI (code-sectioning into the player served no purpose) — FFMediaPlayer.load()/loaded()/loadError()/vulkanDecodeAvailable() became MediaAPI.ffmpegLoaded()/ffmpegError()/vulkanDecode(); binaries extraction stays separate in the binaries module
  • ✨ Added: three-level boot metrics on WaterMedia, names only — step()/steps()/stepName() (which module is loading), taskStep()/taskSteps()/taskName() (which element the module is loading), work()/workTotal()/workName() (units of the demanding task in flight: download/extraction bytes) — enough to emulate three loading bars; currentAPI(), totalWorkSteps() and completedWorkSteps() are removed, module instances are never exposed
  • ✨ Added: WaterMedia.failures() — safe (non-fatal) boot failures as Failure(api, step) records (a failed FFmpeg load, binaries extraction, media cache init or file-server bind), surfaced as [NO] lines in the app's boot splash
  • ✨ Added: FFmpeg extraction publishes real byte progress (compressed bytes read vs. the shipped zip size) through the binaries module's work bar; the boot splash shows the percentage on the active line

⚡ API — Sealing & extension hardening (security)

  • ⚙️ Changed: GFXEngine is now sealed (permits GLEngine, VKEngine, HeadlessGFXEngine) and SFXEngine is now sealed (permits ALEngine, JSEngine) — the public MediaPlayer/TxMediaPlayer/FFMediaPlayer constructors take an engine instance, so sealing closes the injection gate: a caller can only pass WaterMedia's own engines, never a hand-rolled subclass, and can no longer reimplement an engine to work around a rendering bug that isn't WaterMedia's
  • ⚙️ Added: HeadlessGFXEngine — a permitted no-GPU GFXEngine that records uploads into memory instead of a context (server-side probing, CI, headless validation); exposes uploadCount()/lastUpload()/format()/activeFrame() for pipeline introspection and replaces the former test-only stand-in
  • ⚙️ Changed: every concrete API class a developer receives an instance of is now final so it can't be extended — the six ImageReaders (GIF/JPEG/PNG/WEBP/NETPBM/SVGReader), the four NetpbmDecoders, ImageMetadata, NetRequest, NetworkServer, the four WaterMediaAPI facades (Codecs/Media/Network/PlatformAPI), and the built-in IPlatform implementations (WaterPlatform + every web platform)
  • ⚙️ Changed: XCodecException is now sealed (permits UnsupportedFormatException) and YtDlpPlatform is now sealed (permits YouTubePlatform)
  • 🔸 Note: IPlatform (registered by apps through PlatformAPI.register), VKContext (the modder Vulkan-device bridge), and the platform exception hierarchy (PlatformException/MatureContentException) stay open on purpose — they are documented extension points, not instances handed back to callers
  • 🔸 Note: the unnamed-module boundary means sealed only compiles for same-package subclasses, so the cross-package abstract bases (WaterMediaAPI, ImageReader, ImageWriter, NetpbmDecoder) are hardened by finalizing their leaves instead — a developer subclass of the base is inert because WaterMedia never accepts one

⚡ CodecsAPI — JPEG decoder (security hardening)

  • 🐛 Fixed: uncapped 16-bit SOF dimensions overflowed the coefficient/sample/BGRA int math — a few-byte header could wrap new int[...] to 0/negative (ArrayIndexOutOfBoundsException/NegativeArraySizeException) or force multi-GB allocations (OutOfMemoryError); both axes are now hard-capped at 16384 (16K) and rejected with a clean codec error
  • 🐛 Fixed: the scan header's 4-bit DC/AC Huffman table selectors (0..15) were used unchecked to index the [2][4] table array, so a selector of 4..15 threw an uncontrolled ArrayIndexOutOfBoundsException; selectors are now validated to 0..3
  • 🐛 Fixed: a malformed DHT declaring an oversubscribed (non-canonical) Huffman code overflowed the fast-lookup table fill and value indexing with an uncontrolled ArrayIndexOutOfBoundsException; the table builder now rejects oversubscribed codes with a clean codec error
  • 🐛 Fixed: a corrupt DC Huffman table could yield an out-of-range magnitude category (SSSS > 15) feeding a bogus bit count into the coefficient extend; the category is now validated to 0..15

⚡ CodecsAPI — GIF decoder (security hardening)

  • 🐛 Fixed: uncapped 16-bit logical-screen dimensions overflowed width*height*4 int math (NegativeArraySizeException) or forced multi-GB allocations (OutOfMemoryError) for the canvas and LZW index buffers; the canvas is now hard-capped at 16384 (16K) per axis and rejected with a clean codec error
  • 🐛 Fixed: an animation frame whose image descriptor extends beyond the logical screen overflowed the width*height LZW allocation; frames are now bounds-checked against the canvas and rejected with a clean codec error
  • ⚙️ Added: config decoders.gif.clampImageDesc (default off) — when enabled, an out-of-bounds frame is clamped to the canvas (best-effort, logged as WARN, may show artifacts) instead of failing the decode
  • 🐛 Fixed: ScreenDescriptor, ImageDescriptor and ColorTable threw unchecked IllegalArgumentException on malformed data, escaping the IOException contract and crashing the caller; validation now surfaces as XCodecException at the parse boundary
  • 🐛 Fixed: the sub-block buffer growth (next <<= 1) could overflow to a negative size (NegativeArraySizeException) on pathologically large inputs; growth is now overflow-guarded and bounded

⚡ CodecsAPI — PNG/APNG decoder (security hardening)

  • 🐛 Fixed: APNG fcTL frame geometry was never validated against the canvas — a frame declaring a size larger than the canvas, or negative x/y offsets, drove out-of-bounds writes (uncontrolled ArrayIndexOutOfBoundsException) in the decode, blend and dispose paths; every frame is now bounds-checked (the existing FCTL.validate is now actually called, and hardened to long math so offset+size can't wrap past the check) and rejected with a clean codec error
  • 🐛 Fixed: uncapped IHDR dimensions — the raw 32-bit width/height overflowed width*height*4 int math (NegativeArraySizeException) or forced multi-GB allocations (OutOfMemoryError); both axes are now hard-capped at 16384 (16K) and rejected with a clean codec error
  • 🐛 Fixed: IDAT/fdAT decompression bomb — inflation grew the output buffer unbounded, so a tiny image could inflate to gigabytes (OutOfMemoryError); output is now bounded to the exact size a well-formed frame decompresses to (computed as long to avoid overflow on 16K depth-16 frames) and aborts with a clean codec error past it
  • 🐛 Fixed: zTXt/iTXt decompression bomb — compressed text metadata inflated with no output limit; both are now capped at 2 MB decompressed and throw past it
  • 🐛 Fixed: indexed bKGD with a palette index beyond the PLTE (or with no PLTE at all) hit the unchecked PLTE.getColor with an uncontrolled ArrayIndexOutOfBoundsException — validated at construction (only reachable with decoders.png.useBKGDChunk enabled)

⚡ CodecsAPI — WEBP decoder (security hardening)

  • 🐛 Fixed: uncapped 24-bit VP8X canvas and ANMF frame dimensions — a crafted file of a few bytes could declare up to 16777216×16777216, overflowing width*height*4 int math and forcing multi-GB allocations (OutOfMemoryError) or uncontrolled ArrayIndexOutOfBoundsException; both are now hard-capped at 16384 (16K) per axis and rejected with a clean codec error
  • 🐛 Fixed: lossless images using bundled palettes (16 colors or fewer) crashed with an uncontrolled IndexOutOfBoundsException (the image decodes at a reduced width but the palette inverse copied back at full width) — they now fail with a clean codec error; proper bundling decode is pending
  • 🐛 Fixed: VP8 lossy frames declaring broken partition sizes (partition 0 or token partitions overrunning the chunk, truncated size table, empty partitions) escaped the IOException contract with uncontrolled IllegalArgumentException/BufferUnderflowException — sizes are now validated up front and fail with a clean codec error
  • 🐛 Fixed: VP8L bodies shorter than the 5-byte header inside extended (VP8X) or animation (ANMF) containers crashed with an uncontrolled IllegalArgumentException when skipping the header (only the simple VP8L path validated it) — now rejected with a clean codec error
  • ⚙️ Added: config decoders.vp8.brokenTokens (default off) — instead of failing, attempts to repair frames with broken token partition sizes: declared sizes are clamped to the bytes actually available and unusable partitions are replaced with a duplicate of the previous one or with fake zeroed data; decode continues best-effort and may show artifacts, every repair is logged as WARN
  • 🐛 Fixed: the container-declared dimensions (VP8X canvas or ANMF frame) were never checked against the dimensions the embedded VP8/VP8L bitstream actually carries — output buffers are sized from the container, so a crafted mismatch overran the YUV plane copy (IndexOutOfBoundsException), the BGRA buffer limit (IllegalArgumentException) or the animation compositor (ArrayIndexOutOfBoundsException), and a smaller canvas silently delivered truncated pixels; both sides must now agree (as the spec requires) and mismatches fail with a clean codec error
  • 🐛 Fixed: ANMF sub-chunk sizes were read as signed 32-bit and used unvalidated — a negative size made the sub-chunk cursor stop advancing (infinite loop / thread hang) or walk into negative indices (ArrayIndexOutOfBoundsException), and a huge size wrapped the bounds check; sizes are now validated with overflow-free math before use
  • 🐛 Fixed: animation frames extending past the canvas were silently clipped while still decoding (and allocating) the full off-canvas frame; the spec requires frames to lie fully inside the canvas, so they are now rejected with a clean codec error — which also bounds every per-frame allocation by the validated canvas size
  • 🐛 Fixed: the animation pre-scan crashed the constructor with an uncontrolled IllegalArgumentException on an odd-sized VP8X chunk truncated exactly at its end (the RIFF padding byte seek landed one past the buffer limit); the scan now clamps and degrades to a static-image summary
  • 🐛 Fixed: the 16K-per-axis cap alone still allowed 16384×16384 declarations — ~1 GiB per pixel buffer and ~2 GiB on the animated path (canvas + output) from a few-byte header (OutOfMemoryError); a total-pixel cap (64 Mpx, e.g. 8192×8192) now rejects them at construction, and zero-dimension VP8 frames are rejected instead of producing empty buffers
  • 🐛 Fixed: VP8L transform declarations were unbounded — repeating transform types let a tiny file queue thousands of full-image inverse passes (CPU exhaustion); each transform type may appear at most once per the spec and duplicates now fail with a clean codec error
  • ⚙️ Changed: VP8L huffman group counts are now enforced through a feasibility gate — each group costs at least 20 bits, so a count the remaining bitstream cannot physically encode (e.g. a 1×1 entropy image declaring 65536 groups ≈ 200 MB of tables from a few-KB file) fails before allocating instead of after; counts past the recommended 256 still log a WARN

⚡ MediaAPI — Engines (rewrite: construction, naming & BCn pixel formats)

  • ⚙️ Changed: engine creation is centralized in MediaAPI factories — glEngine(thread, ex), vkEngine(ctx), jfxEngine(onFrame), awtEngine(onFrame), headlessEngine(preload), alEngine([buffers]), jsEngine([bufferMs]); every Builder class and buildDefault() is removed (they were one-argument wrappers around one-argument constructors)
  • ⚙️ Changed: the client-side check moved into the sealed GFXEngine/SFXEngine base constructors — one enforcement point covering every engine (previously only GLEngine/VKEngine checked, each on its own), impossible to bypass even with a direct new; HeadlessGFXEngine is the sanctioned server-side exception via a package-private unchecked path
  • ⚙️ Renamed (GFXEngine): setVideoFormatformat(...), supportsFormatsupports, supportsFrameTextures()preload(), uploadFrameTexturespreload(frames, stride), useFrameTextureframe(index), requiredBufferAlignment()alignment(), releaseBuffer(buf)release(buf), bitsPerComponent()bits() — record-like naming, overload-paired with the getters
  • ⚙️ Renamed (SFXEngine): setAudioFormatformat(type, channels, rate), mirroring the GFX contract
  • ✨ Added: PixelFormat now carries intrinsic layout data — planes(), blockBytes(), compressed() — and gains the block-compressed constants BC1/BC2/BC3/BC5/BC7 ahead of the ISPC encoders, so BCn is a first-class pixel format instead of an external codec-only concept
  • ⚙️ Removed: GFXEngine#supportsCompressedTextures(String) / uploadCompressedFrames(ByteBuffer[], String, int) — compressed textures ride the standard path now: supports(PixelFormat.BC*) + format(BC*, w, h) + preload(blocks, 0), with blockBytes intrinsic to the enum; the TxMediaPlayer codec-cache replay maps the DDS codec id straight to its PixelFormat constant
  • ⚙️ Changed: GLEngine's five conversion shaders share one uniform scheme (plane0..plane3/bitScale/uvSwap/outputWidth) — undeclared locations resolve to -1 and are silently ignored per the GL spec, so a single compile/bind path replaces the three per-format switches (~25 uniform fields and ~150 duplicated lines dropped)
  • ⚙️ Changed: single-use helpers aggressively inlined across both GPU engines (GLEngine: quad/PBO init, shader release, hub acquire/release, async frame-texture scheduling; VKEngine: init, buildFormat, updateDescriptorSet, ensureComputePipeline, the SPIR-V cache wrappers) — locals stay in one frame for the JIT and the read flow is linear; big cold well-named steps (ensureYcbcr, ring lifecycle) stay extracted

⚡ MediaAPI — Engines (VKEngine)

  • ⚙️ Renamed: VKContext#instance()vkInstance() and VKContext#device()vkDevice() — avoids overloading Minecraft 26.x's VulkanDevice#instance() by return type alone (the JVM resolves by full descriptor, so the mixin-merged class is legal bytecode, but same-name methods muddy stack traces and crash reports); a mod can now implement VKContext directly on it through a mixin (MC's own vkDevice() accessor satisfies the interface verbatim) and pass the cast device straight to MediaAPI.vkEngine; the README documents the full pattern

⚡ MediaAPI — Engines (GLEngine)

  • ⚙️ Removed: the 9 GL proxy callbacks (setGenTexture/setBindTexture/setTexParameter/setPixelStore/setDelTexture/setActiveTexture/setBindVertexArray/setBindFrameBuffer/setBindBuffer) and the BindConsumer/TexParamConsumer interfaces — the engine is now self-contained; MediaAPI.glEngine(renderThread, renderThreadEx) is the whole contract
  • ⚙️ Added: exact GL state capture/restore around every upload wave — texture bindings, active unit, sampler bindings, pixel-store, PBO/ARRAY_BUFFER bindings, VAO, READ/DRAW framebuffers, program, viewport and draw toggles are read once and restored to their exact prior values, so any host-side skip-if-equal cache (Minecraft GlStateManager, Sodium/Iris trackers) stays truthful without integration
  • ⚙️ Added: per-render-thread drain hub — all engines sharing a render thread drain through one batched task per frame inside a single capture/restore envelope, keeping the glGet* cost constant per frame (not per player) at any scale; a broken engine no longer aborts the rest of the wave
  • ⚙️ Added: provably-safe deletion of exposed texture names — storage is freed immediately on release (zero-size respec) and the name is deleted the moment no texture unit still binds it (which, given exact state restore, proves no host binding cache references it), so a cached binding can never point at a driver-reused name; pending names are tracked per GLCapabilities identity so they never leak across recreated contexts
  • 🐛 Fixed: the YUV→RGBA convert pass ran with whatever blend/scissor/colorMask/cull/polygon-mode/sampler state the host left behind — frames could come out clipped, blended or mis-sampled; the pass now forces a clean draw state and restores the host's exactly
  • 🐛 Fixed: a queued upload draining after release() resurrected GL resources (leaked textures on a dead engine) — engines are now terminally flagged and stale render tasks become no-ops
  • ⚙️ Changed: GLEngine construction now rejects a non-null renderThread without an executor instead of failing later on submit

⚡ MediaAPI — Engines (SFXEngine)

  • ⚙️ Added: JSEngine — a native, dependency-free SFXEngine backed by the Java Sound API (javax.sound.sampled), playing decoded PCM straight through the OS mixer (WASAPI/DirectSound, ALSA/PulseAudio, CoreAudio) with no OpenAL context and no external library; a first-class alternative to ALEngine, usable even when OpenAL is available
  • ⚙️ Added: stream-based SFXEngine backend — format(...) opens a fresh SourceDataLine (reopens on format change); uploads are non-blocking via available() backpressure, mirroring ALEngine's buffer-pool contract so the clock keeps tracking the audible position through pendingMs() (framesWritten − getLongFramePosition(), rebased on flush()); volume via MASTER_GAIN; conservative U8/S16 mono/stereo capability tables so format negotiation always lands on an openable line
  • ⚙️ Added: standalone app — an "Audio engine" selector (Settings → App → General) switches between OPENAL and JAVASOUND; the choice (AppContext.audioEngine) feeds the Supplier<SFXEngine> at player creation, so it applies to the next media opened
  • ⚙️ Added: SFXEngine#speed() — reports whether the backend can change the playback speed (ALEnginetrue, JSEnginefalse), so players and UIs can honor engines stuck at 1.0× instead of desyncing against them
  • ⚙️ Added: MediaPlayer#canSpeed()true only when the source and the audio engine both allow a speed change (live streams and speed-less engines report false); speed(float) now refuses instead of scaling the playback clock against audio stuck at 1.0×, which caused unfixable stuttering on FFMediaPlayer under JSEngine
  • ⚙️ Added: standalone app — the player screen's speed dropdown locks (not clickable, drawn dimmed) while the current media reports canSpeed() == false
  • 🔸 Note: the Java Sound backend has no portable pitch/rate control, so speed(float) is a documented no-op (playback stays at 1.0×) and there is no per-source spatialization; use ALEngine when those are required

⚡ MediaAPI — Engines (Software: JavaFX / AWT)

  • ✨ Added: JFXEngine — a GFXEngine that renders decoded frames into a JavaFX Image; the engine's direct BGRA buffer backs a PixelBuffer with no copy, so binding image() to an ImageView shows video after a single updateBuffer on the FX thread — a pure JavaFX app can embed WaterMedia with no OpenGL/Vulkan context
  • ✨ Added: AWTEngine — a GFXEngine that renders into an AWT BufferedImage (TYPE_INT_ARGB) for Swing/AWT apps; uses only java.desktop, so it needs no extra dependency
  • ⚙️ Added: shared SWEngine base (sealed, permits JFXEngine/AWTEngine) — accepts only single-plane BGRA/RGBA and declines every YUV/planar format, so the decoder's scaler pre-converts frames to BGRA; each frame is copied into a reusable direct buffer and published, firing an optional onFrame hook for repaint. texture() returns a non-zero sentinel (there is no GPU texture)
  • ⚙️ Added: JavaFX is a compile-only dependency (openjfx 21 LTS, host-classified — matches the Java 21 runtime; a newer JavaFX would fail with UnsupportedClassVersionError) — WaterMedia compiles JFXEngine against it but never bundles it; a consuming JavaFX app supplies its own runtime, and JFXEngine loads FX classes only when instantiated

⚡ PlatformsAPI — Web platforms (Imgur)

  • 🐛 Fixed: most Imgur links failed to resolve even though the API answered HTTP 200 — /gallery/{id} posts holding a single image (the most common kind on Imgur) carry the image fields directly on data instead of an images[] array and were rejected as "empty or unsuccessful"; both response shapes are now detected and resolved
  • 🐛 Fixed: direct image links (imgur.com/{id}) crashed during JSON deserialization — the image endpoint answers "ad_type": null and Gson refuses null for a primitive record component; the data records now bind only the fields actually consumed, making them immune to Imgur's endpoint-dependent nulls
  • 🐛 Fixed: hidden albums (/a/{id} links never posted to the gallery) returned 404 — /a/ links now resolve through the album endpoint instead of the gallery one
  • ⚙️ Added: tag links (imgur.com/t/{tag}/{id}) resolve through the gallery flow; the legacy #/t/ fragment detection (broken — URI#getFragment() never contains #) was removed
  • ⚙️ Added: www.imgur.com / m.imgur.com hosts are claimed, title slugs and file extensions are stripped from ids, and URLs without a media id are ignored with a warning instead of hitting the API
  • ⚙️ Added: NSFW posts and images now honor platforms.allowMatureContent and throw MatureContentException when disabled — same gate as Kick/Twitch

⚡ PlatformsAPI — Web platforms (Medal)

  • ✨ Added: MedalPlatform — resolves Medal.tv game clips through the public medal.tv/api/content/<id> endpoint (no auth); claims every URL shape (/games/<game>/clips/<id>, any locale prefix like /es/, the short /clip|/clips/<id> links, and ?contentId=<id>), exposes the HLS ladder (source/540p/360p/240p) as quality variants like Kick, with the progressive source MP4 and the social-share MP4 as ordered fallbacks, full metadata (title, description, author, duration, thumbnail, posted date) and URL-expiry honored; DMCA-takedown and login-walled clips throw a clear PlatformException
  • 🔸 Note: Medal's contentUrl<height>p MP4 rungs are ignored — they serve the source file for every rung when no real ladder was transcoded, so the HLS master is used as the single source of truth for qualities

⚡ PlatformsAPI / Tools — Shared JSON tooling

  • ⚙️ Changed: Imgur, Kick (channel/VOD/clip/search), Medal, Sendvid, Streamable and Twitter dropped their identical private fetch helpers and resolve through fetchJson; NetRequest, PornHubPlatform, TwitchPlatform and the standalone app (AppContext) dropped their private new Gson() copies and parse/write through JsonTool

⚡ App — AWT popup player (showcase)

  • ✨ Added: opening an MRL can pop out a native AWT/Swing player window (AWTPlayerWindow) instead of the in-app GPU screen — a MediaPlayer driven by an AWTEngine, with an aspect-fit video surface and a minimal transport bar (play/pause, seek, volume, time). Showcases embedding WaterMedia with no GPU backend and exercises AWTEngine end-to-end inside the product
  • ⚙️ Added: AppContext.playerTarget (IN_APP/AWT) with a segmented Player output control in the settings engines section (persisted in watermedia-app.toml); the PLAYER navigation branches to the popup when set to AWT. The window disposes on close without touching the host app, and its blocking player teardown runs off the EDT

⚡ GENERAL

  • 🛠️ Minecraft/Sodium GL state can no longer be desynced by video playback — no GlStateManager wiring needed anymore, and the overhead stays flat no matter how many screens play at once
  • 🛠️ Hardened the WebP, PNG/APNG, GIF and JPEG decoders against malicious files — crafted images can no longer force gigabyte memory allocations, decompression bombs or crash threads with unexpected exceptions; every malformed file now surfaces as a clean codec error
  • 🐛 Fixed: Imgur links work again — gallery posts, albums (including hidden ones), single images and tag links all resolve now
Type
Name
Game Versions
Mod Loaders
Size
Downloads
Uploaded