Skip to content

fix(player): force NV12 pixel format for subtitle rendering - #1476

Open
LTe wants to merge 1 commit into
streamyfin:developfrom
LTe:subtititles-rendering
Open

fix(player): force NV12 pixel format for subtitle rendering#1476
LTe wants to merge 1 commit into
streamyfin:developfrom
LTe:subtititles-rendering

Conversation

@LTe

@LTe LTe commented Mar 6, 2026

Copy link
Copy Markdown

📦 Pull Request

🔖 Summary

Force NV12 pixel format in the mpv video filter pipeline to ensure subtitles render correctly when software decoding produces planar YUV (y420p) frames.

🏷️ Ticket / Issue

  • Related: Subtitle rendering broken during software decoding fallback on iOS

🛠️ What's Changed

  • Type: fix
  • Scope: player
  • Short summary: Added vf=format=fmt=nv12 option to mpv initialization to convert all decoded frames to NV12 (biplanar) before they reach the CoreImage-based subtitle compositing pipeline.

📋 Details

When mpv falls back to software decoding (e.g., for codecs not supported by VideoToolbox, or on the iOS Simulator which lacks hardware decoding), decoded frames use the y420p pixel format (triplanar YUV — 3 separate planes: Y, U, V). The subtitle overlay system (composite OSD) uses Apple's CoreImage to blend subtitles onto video frames, but CoreImage does not support triplanar y420p — it only works with biplanar formats like NV12 (420v).

This causes subtitles to silently fail to render whenever software decoding is active — notably, subtitles were completely broken on the iOS Simulator.

The fix inserts a video filter (vf=format=fmt=nv12) that converts all frames to NV12 before rendering, ensuring CoreImage always receives a compatible pixel format. This fixes subtitle rendering on both the Simulator and on physical devices during software decoding fallback.

Format Type Planes CoreImage Support
NV12 (420v) Biplanar 2 (Y + UV interleaved) ✅ Yes
y420p Triplanar 3 (Y + U + V separate) ❌ No

⚠️ Breaking Changes

None.

🔐 Security & Privacy Impact

None.

⚡ Performance Impact

Minimal. The format conversion (y420p → nv12) is a lightweight pixel rearrangement (interleaving U and V planes) that occurs in the CPU pipeline. When hardware decoding is active, frames are already NV12, so the filter is effectively a no-op.

🖼️ Screenshots / GIFs (if UI)

Subtitles now render correctly during software decoding fallback. Previously they were invisible.

image

✅ Checklist

  • I've read the contribution guidelines
  • Code follows project style and passes lint/format (bun scripts)
  • Type checks pass (tsc/biome/etc.)
  • Docs updated (README/ADR/usage/API)
  • No secrets/credentials included; env vars documented
  • Release notes/CHANGELOG entry added (if applicable)
  • Verified locally that changes behave as expected

🔍 Testing Instructions

  1. git fetch origin pull/<PR_ID>/head:branchname && git checkout branchname
  2. Install deps: bun install
  3. Build and run on iOS: bun run ios
  4. Play a video that triggers software decoding (e.g., a codec not supported by VideoToolbox)
  5. Enable subtitles
  6. Verification steps:
    • Subtitles render correctly during software decoding
    • Subtitles still render correctly during hardware decoding
    • No visual artifacts or color shifts in the video
    • No noticeable performance degradation

⚙️ Deployment Notes

No additional configuration needed. The change is contained within the mpv player native module initialization on iOS.

📝 Additional Notes

  • This only affects the iOS mpv player module (modules/mpv-player/ios/MPVLayerRenderer.swift)
  • Hardware-decoded frames are already NV12, so the filter is a no-op in that path
  • Reference: Apple's CoreImage/CoreVideo stack was designed around biplanar formats, which is what VideoToolbox hardware decoders natively produce
  • This PR was written with help of Claude Code.

CoreImage (used by composite OSD) doesn't support y420 (planar YUV)
but does support NV12 (420v biplanar). This ensures subtitles render
correctly when software decoding produces y420p frames.
Copilot AI review requested due to automatic review settings March 6, 2026 17:54

Copilot AI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@github-actions github-actions Bot added the ⚔️ merge-conflict Pull request has merge conflicts label May 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.

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

Labels

⚔️ merge-conflict Pull request has merge conflicts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants