Skip to content

fix(replay): Release MediaMuxer when no frames are encoded#5583

Merged
runningcode merged 2 commits into
mainfrom
no/fix-replay-muxer-resource-leak
Jun 22, 2026
Merged

fix(replay): Release MediaMuxer when no frames are encoded#5583
runningcode merged 2 commits into
mainfrom
no/fix-replay-muxer-resource-leak

Conversation

@runningcode

@runningcode runningcode commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Fixes JAVA-580

📜 Description

The MediaMuxer inside SimpleMp4FrameMuxer is constructed eagerly when the video encoder is created (CloseGuard.open registers a guard expecting release()), but release() was only reachable on the happy path. Two cases leaked it:

  • ReplayCache.createVideoOf returned early when frameCount == 0 (frames exist but none could be encoded) without calling encoder?.release().
  • SimpleMp4FrameMuxer.release() called muxer.stop() before muxer.release(). MediaMuxer.stop() throws IllegalStateException if the muxer was never started (i.e. no frame was ever muxed, so INFO_OUTPUT_FORMAT_CHANGED never fired), so muxer.release() was skipped. The exception was then swallowed by SimpleVideoEncoder.release()'s catch block.

The fix guards stop() behind the started flag so release() is always reached, and releases the encoder on the no-frames return path.

💡 Motivation and Context

Surfaces as a CloseGuard warning: A resource was acquired at attached stack trace but never released ... Explicit termination method 'release' not called, pointing at the MediaMuxer constructor in SimpleMp4FrameMuxer. It is a latent resource leak in production whenever a segment ends up with zero encodable frames, and it shows up consistently in Robolectric tests where the encode pipeline doesn't start the muxer.

💚 How did you test it?

The existing ReplayCacheTest."when screenshot is corrupted, deletes it immediately" exercises the frames-present-but-none-encoded path (frameCount == 0 after the encoder is created), which previously triggered the leak.

📝 Checklist

  • I added GH Issue ID & Linear ID
  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.

🔮 Next steps

runningcode and others added 2 commits June 22, 2026 12:07
The MediaMuxer is created when the video encoder is constructed, but its
release() was reachable only on the happy path. Two cases leaked it:

- createVideoOf returned early when frameCount was 0 without releasing the
  encoder.
- SimpleMp4FrameMuxer.release() called muxer.stop() before muxer.release().
  stop() throws if the muxer was never started (no frame ever muxed), so
  release() was skipped.

This surfaced as a CloseGuard "resource was acquired but never released"
warning. Guard stop() behind the started flag so release() is always
reached, and release the encoder on the no-frames return path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sentry

sentry Bot commented Jun 22, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
SDK Size io.sentry.tests.size 8.44.1 (1) release

⚙️ sentry-android Build Distribution Settings

@github-actions

Copy link
Copy Markdown
Contributor

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 310.74 ms 329.02 ms 18.28 ms
Size 0 B 0 B 0 B

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
18c0bc2 306.73 ms 349.77 ms 43.03 ms
0eaac1e 316.82 ms 357.34 ms 40.52 ms
d15471f 303.49 ms 439.08 ms 135.59 ms
fc5ccaf 276.52 ms 370.46 ms 93.93 ms
e2dce0b 308.96 ms 360.10 ms 51.14 ms
5b1a06b 352.27 ms 413.70 ms 61.43 ms
37ec571 366.04 ms 424.28 ms 58.23 ms
9fbb112 361.43 ms 427.57 ms 66.14 ms
bbc35bb 324.88 ms 425.73 ms 100.85 ms
ff8eea4 313.42 ms 337.08 ms 23.66 ms

App size

Revision Plain With Sentry Diff
18c0bc2 1.58 MiB 2.13 MiB 557.33 KiB
0eaac1e 1.58 MiB 2.19 MiB 619.17 KiB
d15471f 1.58 MiB 2.13 MiB 559.54 KiB
fc5ccaf 1.58 MiB 2.13 MiB 557.54 KiB
e2dce0b 0 B 0 B 0 B
5b1a06b 0 B 0 B 0 B
37ec571 0 B 0 B 0 B
9fbb112 1.58 MiB 2.11 MiB 539.18 KiB
bbc35bb 1.58 MiB 2.12 MiB 553.01 KiB
ff8eea4 1.58 MiB 2.28 MiB 718.64 KiB

@linear-code

linear-code Bot commented Jun 22, 2026

Copy link
Copy Markdown

JAVA-580

@romtsn romtsn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nice find!

@runningcode runningcode enabled auto-merge (squash) June 22, 2026 11:55
@runningcode runningcode merged commit f037273 into main Jun 22, 2026
69 of 71 checks passed
@runningcode runningcode deleted the no/fix-replay-muxer-resource-leak branch June 22, 2026 12:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants