Skip to content

Releases: batical/react-native-skia-webgpu-video

v0.1.0-beta.4 — Skia 3.7.4, an EGL-free Android export a third faster

Choose a tag to compare

@batical batical released this 24 Oct 18:57
2c53c16

Requires react-native-skia@3.7.4 and react-native-webgpu@0.19.0.

Install

npm install --save-exact react-native-skia@3.7.4 react-native-webgpu@0.19.0 react-native-reanimated@4.5.3 react-native-worklets@0.11.3
npm install --save-exact react-native-skia-webgpu-video@0.1.0-beta.4
npx --no-install skia-video-patch-canvas

Add the last command to your app's postinstall, then rebuild the native app; see the README. The archive attached here is the same package, installable by URL.

Changes since v0.1.0-beta.3

Skia 3.7.4 / WebGPU 0.19.0: EGL-free Android export, a third faster (#26)

  • Requires react-native-skia@3.7.4 and react-native-webgpu@0.19.0 (Graphite and Dawn stay at 157). Run npx --no-install skia-video-patch-canvas again and rebuild the native app. The local Skia patch shrinks from 7 files to 3: canvas disposal and the image provider ownership fix are now upstream.
  • Android exports are a third faster, up to 45% with 4K sources (Pixel 8a, medians, beta.3 → beta.4). iOS is unchanged.
    • 8 × 4K clips at native size into a 1080 output: 9.8 → 7.0 s.
    • The same clips capped at 1280: 12.7 → 7.0 s.
    • 3 simultaneous 4K clips: 5.6 → 3.5 s.
    • HEVC 1080p: 3.12 → 2.08 s.
    • 640×360: 1.25 → 0.85 s.
    • One 4K clip exported at 4K: 25.3–25.8 → 22.3–23.0 s.
  • Android export renders straight into the codec's input Surface through WebGPU (MakeWebGPUCanvasContextFromNativeWindow, a Vulkan swapchain). There is no EGL context and no lent AHardwareBuffer anymore.
    • First problem found: the swapchain tagged frames sRGB, full range, so the codec wrote yuvj420p files 3–11% larger.
    • The window's dataspace is now reset before each frame. Files stay Rec.709 limited range, byte-identical to beta.3.
  • Fixed: the first 3.7.4 build was 24% slower than beta.3 on 8 native 4K clips.
    • A Perfetto trace found the cause. The export waited for the GPU after every frame, then left it idle while decoding the next one. Mali's frequency governor therefore kept the GPU at its lowest clock, 160 MHz, three quarters of the time.
    • The export now waits for a frame's GPU work only once it has decoded the next frame, so the GPU draws while the CPU decodes. A decoder buffer is still handed back only after the GPU is done with it.
  • The Android export thread reports its frames to Android's performance hints (ADPF, Android 13 and later). It gets faster cores, and from Android 15 more GPU capacity: 15–29% on most export cases. This is best effort; nothing changes on devices without hint sessions.
  • maxLongSide and resolution items now decode through ImageReader on Android (8-bit SDR H.264/HEVC). The codec keeps its YUV buffers, and the WebGPU conversion scales them instead of an OpenGL render pass. For 8 × 4K clips capped at 1280, decoding drops from 17 to 8.7 ms per frame. Capped clips keep their frame sizes, but their pixels change slightly, since the scaling is now bilinear in WebGPU.
  • Benchmark: two new cases, export-4k-h264 and export-4k-hevc, export a 30 s real-footage 4K clip at 4K. The 65 existing workloads are unchanged.

Release 0.1.0-beta.4 (#27)

Version bump for 0.1.0-beta.4. npm run ship merges it once CI passes, then tags and publishes it.

Not yet qualified

  • Memory over long sessions (last checked on 3.0.6).
  • HDR beyond the HLG to SDR conversion, audible output and A/V synchronization.
  • Known Pixel 8a issues, all predating beta.3: a seek while playing a 4K sequence leaves the clock stalled, and a paused seek into a 120-clip lazy montage does not show its frame.
  • An iOS export with yuv frames (the default) spends 2–4 ms more per frame than with framePixelFormat: 'rgba'.

Full changelog: v0.1.0-beta.3...v0.1.0-beta.4

SHA-256 of the archive: 32a48bfac830fbe82c5b22e0425dab80614ecea6f5d269eee4e807dfdc1a631e

v0.1.0-beta.3 — iOS decodes to YUV by default, frame-accurate preview

Choose a tag to compare

@batical batical released this 21 Oct 20:05
ce179f5

Requires react-native-skia@3.7.1 and react-native-webgpu@0.17.0.

Install

npm install --save-exact react-native-skia@3.7.1 react-native-webgpu@0.17.0 react-native-reanimated@4.5.3 react-native-worklets@0.11.3
npm install --save-exact react-native-skia-webgpu-video@0.1.0-beta.3
npx --no-install skia-video-patch-canvas

Add the last command to your app's postinstall, then rebuild the native app; see the README. The archive attached here is the same package, installable by URL.

Changes since v0.1.0-beta.2

Update LICENSE (#21)

See #21.

Invite users to share projects in Show and tell (#23)

  • README: new Built with it section (before Development) that links to the thread; featured projects get listed there.
  • .github/DISCUSSION_TEMPLATE/show-and-tell.yml: form for new Show and tell posts (link, description, platforms, screenshot).

iOS decodes to YUV by default, and a frame-accurate preview (#24)

  • iOS now decodes composition frames to YUV (NV12) by default, as Android always has. A 4K frame takes 12.4 MB instead of 33.2 MB of BGRA, so the decoder keeps five 4K frames ahead instead of one or two. On an iPhone 15 Pro, 4K 60 fps previews show 77–90% of their frames on time instead of 46–48%, and the three compositions that stopped on the video memory budget now play (61/61 benchmark cases). Camera footage (Rec.709 or HLG) renders the same. To keep the previous behavior, set framePixelFormat: 'rgba' on the composition.
  • New framePixelFormat option on VideoComposition: 'auto' (the default, YUV on both platforms), 'yuv' or 'rgba', applied to the preview and the export.
  • Trade-offs of YUV on iOS: an export spends 2–4 ms more per frame (4K exports unchanged, a 120-clip montage takes 18.1 s instead of 14.0 s), and files tagged Display P3 or sRGB are no longer converted to Rec.709. Use 'rgba' where either matters.
  • Smoother, frame-accurate preview. drawFrame now gets the time its frames were picked at. Read a few milliseconds later, the time ran up to a frame ahead of the video at 60 fps, so overlays drawn from it were a frame off. The preview now draws the frame due at 99–100% of vsyncs at 30, 24 and 60 fps (Pixel 8a before: 51–93%).
  • splitImportAndDraw no longer drops one frame in two when a new frame arrives at every vsync (75 skipped frames in 6 s down to 2).
  • Android 15+: a playing preview asks the display for its highest refresh rate. The Pixel 8a no longer falls to 30 Hz during playback, where a 60 fps clip lost one frame in two, and reaches 120 Hz when Smooth display is on.
  • 120 Hz on ProMotion iPhones: add CADisableMinimumFrameDurationOnPhone (Boolean, YES) to your app's Info.plist. Without it, iOS holds the preview at 60 Hz.
  • iOS: the composition decoder runs at user-interactive priority. At utility, a warm iPhone froze 60 fps clips for 300–500 ms.
  • getVideoResourceStats().native reports the iOS preview's frame ring refusals, empty decode queues and slow decode ticks.

Release 0.1.0-beta.3 (#25)

Version bump for 0.1.0-beta.3. npm run ship merges it once CI passes, then tags and publishes it.

Not yet qualified

  • Memory over long sessions (last checked on 3.0.6).
  • HDR beyond the HLG to SDR conversion, audible output and A/V synchronization.
  • Known Pixel 8a issues, all predating beta.3: a seek while playing a 4K sequence leaves the clock stalled, and a paused seek into a 120-clip lazy montage does not show its frame.
  • An iOS export with yuv frames (the default) spends 2–4 ms more per frame than with framePixelFormat: 'rgba'.

Full changelog: v0.1.0-beta.2...v0.1.0-beta.3

SHA-256 of the archive: b08654749d1b182a10d37eefbfd000921ed6aff3546daedbc06171eb72e8ec8b

v0.1.0-beta.2 — Skia 3.7.1, safer vsync release

Choose a tag to compare

@batical batical released this 20 Oct 16:19
1eeaa3b

Requires react-native-skia@3.7.1 and react-native-webgpu@0.17.0. Thanks to @AmatoGiulio for contributing.

Install

npm install --save-exact react-native-skia@3.7.1 react-native-webgpu@0.17.0 react-native-reanimated@4.5.3 react-native-worklets@0.11.3
npm install --save-exact react-native-skia-webgpu-video@0.1.0-beta.2
npx --no-install skia-video-patch-canvas

Add the last command to your app's postinstall, then rebuild the native app; see the README. The archive attached here is the same package, installable by URL.

Changes since v0.1.0-beta.1

Send the vsync release through handleMessage, and note two option limits (#17, by @AmatoGiulio)

  • Vsync release goes through handleMessage. It was a posted runnable, which the playback thread runs directly: no releasing check, and an exception crashed the app instead of becoming an error event. It is now a PLAYBACK_VSYNC_RELEASE message. vsyncReleasePosted still coalesces it to one pending message.
  • Docs. redrawOnTime says that drawWhenPaused: true overrides it. splitImportAndDraw says that it applies on iOS too.

Move to react-native-skia 3.7.1 (#19)

  • react-native-skia 3.7.1. It contains a single upstream fix, iOS only (#4273): SkiaView now presents at the display's full rate on ProMotion devices. WebGPU stays at 0.17.0, with Dawn and Graphite 157.0.0.
  • Skia patch applied unchanged. The 7 patched files are byte-identical to 3.7.0's, so only the patch's version and file name change (patches/react-native-skia+3.7.1.patch). Our upstream PRs #4274–#4276 are still open, so the patch can't shrink yet.
  • Install pins updated in the README, BUILD_VALIDATION.md, UPSTREAM_NOTES.md and the example's Podfile.lock.

Release in one command: npm run ship (#18)

  • npm run ship does a whole release from a clean main:
    1. It bumps the version in a release/vX PR: package.json, the lockfile and the example's Podfile.lock.
    2. It waits for the Host checks, squash-merges the PR, then runs release.mjs: tag, GitHub release, npm.
    • By default it picks the next prerelease (0.1.0-beta.1 → 0.1.0-beta.2).
    • --dry-run prints the version and the notes, and changes nothing.
    • After a failure, running it again resumes where it stopped.
  • Generated release notes (scripts/release-notes.mjs, npm run release:notes), in the format of the earlier releases:
    • install block built from the pinned peers;
    • every PR since the previous tag, with the list from its Summary or Changes section and its author;
    • the not-yet-qualified list, kept in .github/release-status.md;
    • the archive's SHA-256.
    • release.mjs used to fall back on GitHub's generated notes, which list only titles.
  • Title vX — <summary> (--summary), and a post for X printed after publishing.
  • Already published: running it again on a fully released version now says so, instead of asking for a bump.

Release 0.1.0-beta.2 (#20)

Version bump for 0.1.0-beta.2. npm run ship merges it once CI passes, then tags and publishes it.

Not yet qualified

  • Memory over long sessions (last checked on 3.0.6).
  • HDR, audible output and A/V synchronization.
  • Two known 4K issues: a lazy composition seek times out in 1 of 3 runs on the Pixel 8a, and three simultaneous 4K clips stop on the video memory budget during iPhone playback.

Full changelog: v0.1.0-beta.1...v0.1.0-beta.2

SHA-256 of the archive: 694ef48ac45ee60b13620ae3f029a30007cf70ebba27fd0a63054abc01a9c034

v0.1.0-beta.1 — smoother Android preview, export memory fix

Choose a tag to compare

@batical batical released this 20 Oct 13:29
e000a87

Second beta: a smoother Android preview and a fix for Android exports that stopped on the video memory budget. It still requires react-native-skia@3.7.0 and react-native-webgpu@0.17.0. The JavaScript API only gains two opt-in options.

Install

npm install --save-exact react-native-skia@3.7.0 react-native-webgpu@0.17.0 react-native-reanimated@4.5.3 react-native-worklets@0.11.3
npm install --save-exact react-native-skia-webgpu-video@beta
npx --no-install skia-video-patch-canvas

Add the last command to your app's postinstall, then rebuild the native app. See the README. The archive attached here is the same package, and can be installed by URL.

Changes since v0.1.0-beta.0

Android preview pacing (#15, by @AmatoGiulio)

These measurements are Giulio's, from a OnePlus CPH2709 playing 24 fps footage on a 60 Hz display.

  • Codec callbacks off the UI thread. Without lazy decoders, the codecs called back on the main looper, including the burst of callbacks after each seek or loop. They now have their own thread. Main-thread wake-ups dropped from 337 in 12 s to 7 in 6 s. Pauses of 33–115 ms at the cuts between items went down to about one 33 ms frame per loop or two.
  • Frames released on the display's vsync. Before, they were released on a 10 ms tick that wasn't in step with the display. Frames shown a vsync late went from about 45% to 12–30%.
  • redrawOnTime, new and opt-in. It lets the player skip redrawing at vsyncs where only the clock moved, not the frames. UI thread time per frame went from a 13.7 ms median to 4.7 ms.
  • splitImportAndDraw, new and off by default. A new frame is imported on one vsync and drawn on the next, so the work fits within a 90–120 Hz frame budget. The picture then trails the clock by one vsync.

Fixes (#16)

  • Adaptive refresh rates. The vsync period now comes from the display. When a display drops from 90 or 120 Hz to 60 Hz, as the Pixel 8a does, the old estimate stayed at the high rate and released frames half a period early.
  • "Video memory budget exceeded: android-frame-rgba" during Android exports. A dropped RGBA frame kept its share of the 256 MB budget until the garbage collector ran, so a fast decode could fill the budget with dead frames. When the budget refuses a frame, the library now runs a garbage collection and retries. Frames that are still in use still hit the limit, as before.

Release tooling (#13, #14)

  • npm run release creates the git tag and the GitHub release with the package archive, then publishes that same archive to npm. The npm package and the GitHub archive are therefore identical. A prerelease is published under its own dist-tag, beta, and never under latest.
  • Before tagging, the script checks that npm and gh are logged in and that the version isn't already on npm. It only tags a clean main that matches origin/main, after the host checks pass. It also checks the archive's name, version and patch command.
  • Steps already done are skipped, so a failed publish can simply be run again.
  • After publishing, the script waits for the version to appear on npm. If npm's staged publishing holds it, the script explains how to approve it.

See CONTRIBUTING.

Tests

  • The Android preview is now tested the way an app drives it: frames collected on the main thread at every vsync, with eager and lazy decoders.
  • On a Pixel 8a at 60 Hz, at least 95% of frames are shown on their first vsync. With the old 10 ms tick, about two thirds were.
  • The full Android suite has 1 failure out of 127, down from 8. The remaining one, a 4K preview throughput check, was already failing before.

Not yet qualified

  • Memory over long sessions, last checked on 3.0.6.
  • HDR, audio output, and audio/video synchronization.
  • Two known 4K issues:
    • a lazy composition seek times out in 1 of 3 runs on the Pixel 8a;
    • with three 4K clips playing at once on iPhone, playback stops on the video memory budget.

SHA-256 of the archive: 06d80501b0de8e64fcbf86887419265b9066af2dcd0e73b59b4b3df768ddb636

Full changelog: v0.1.0-beta.0...v0.1.0-beta.1

v0.1.0-beta.0 — first beta (Skia 3.7.0 / WebGPU 0.17.0)

Choose a tag to compare

@batical batical released this 19 Oct 08:41
2408987

First beta. Same code as v0.1.0-alpha.1; it requires react-native-skia@3.7.0 and react-native-webgpu@0.17.0 (both resolve react-native-webgpu-dawn@157.0.0).

Install

npm install --save-exact react-native-skia@3.7.0 react-native-webgpu@0.17.0 react-native-reanimated@4.5.3 react-native-worklets@0.11.3
npm install --save-exact react-native-skia-webgpu-video@beta
npx --no-install skia-video-patch-canvas

Add the last command to your app's postinstall, then rebuild the native app; see the README. The archive attached here is the same package, installable by URL.

Changes since v0.1.0-alpha.1

  • The skia-video-patch-canvas command now survives npm publish (its bin path lost a leading ./ that npm rejected).
  • The README installs from npm and lists what this beta has not qualified yet.

Not yet qualified

  • Memory over long sessions (last checked on 3.0.6).
  • HDR, audible output and A/V synchronization.
  • Two known 4K issues: a lazy composition seek times out in 1 of 3 runs on the Pixel 8a, and three simultaneous 4K clips stop on the video memory budget during iPhone playback.

SHA-256 of the archive: 971d334373ad25c47c75b286f31cdf6b6ee50aa9e38e13bc752308d3a1375616

v0.1.0-alpha.1 — Skia 3.7.0 / WebGPU 0.17.0

Choose a tag to compare

@batical batical released this 19 Oct 08:09
d51175a

First versioned alpha of the library. It requires react-native-skia@3.7.0 and react-native-webgpu@0.17.0. Both resolve react-native-webgpu-dawn@157.0.0; install them together.

Install

npm install --save-exact react-native-skia@3.7.0 react-native-webgpu@0.17.0 react-native-reanimated@4.5.3 react-native-worklets@0.11.3
npm install https://github.com/batical/react-native-skia-webgpu-video/releases/download/v0.1.0-alpha.1/react-native-skia-webgpu-video-0.1.0-alpha.1.tgz
npx --no-install skia-video-patch-canvas

Then rebuild the native app; see the README. The last command applies the version-checked Skia patch shipped in the archive; add it to your app's postinstall so it runs after clean installs. Requirements: React Native 0.83–0.86 with the New Architecture and Hermes, Android API 28+ with Vulkan 1.1.

What's new

  • Skia 3.7.0 / WebGPU 0.17.0, with Graphite and Dawn 157.0.0.
  • Smoother canvases on Android 10. Dawn asks for three swapchain images, so the main thread no longer waits for the previous frame's buffer on every frame. Video previews benefit with no code change.
  • Export speed is unchanged from 3.6.1 on an iPhone 15 Pro and a Pixel 8a. Decoded colors are checked against CPU decoding on the Pixel: 10/10 cases within ±1.

Details: audit · verification · #11

Alpha: long-session memory, HDR, audible output and A/V sync are not yet qualified on this build.

SHA-256 of the archive: 163080e05725980c719482996feba51b6ad3338b62c0e2c38d0c76dfc6e5fbbb