stride3d/stride · error · InvalidOperationException
mediaCodec has already been initialized
Error message
mediaCodec has already been initialized
What it means
MediaCodecVideoBackend.Initialize was called on a backend that already has a mediaSynchronizer or mediaCodecVideoExtractor, i.e. it is already initialized (or mid-initialization). The backend forbids double initialization; it must be reset or a new instance used.
Solutions
- Dispose/reset the backend before re-calling Initialize
- Create a fresh MediaCodecVideoBackend per video/play session
- If a previous Initialize partially failed, call the backend's cleanup path before retrying
- Guard callers: only Initialize once per instance lifecycle
Example fix
// before backend.Initialize(url, 0, len); backend.Initialize(url2, 0, len); // throws // after backend.Dispose(); backend.Initialize(url2, 0, len);
Defensive patterns
Strategy: validation
Validate before calling
if (backend.IsInitialized) backend.Dispose(); backend.Initialize(url, startPosition, length);
Type guard
bool CanInitialize(MediaCodecVideoBackend b) => !b.IsInitialized;
Try / catch
try { backend.Initialize(url, start, len); }
catch (InvalidOperationException ex) when (ex.Message.Contains("already been initialized"))
{ backend.Dispose(); backend.Initialize(url, start, len); } Prevention
- Recreate backends per play session instead of re-initializing
- Clean up partially-failed initializations before retrying
- Track lifecycle state explicitly in owning components
When it happens
Trigger: Calling Initialize twice on the same backend without Dispose/Reset; a partially failed earlier Initialize that created one of the fields and threw after; reusing a cached backend for a new URL.
Common situations: Replaying a video through the same backend object; scene reload re-initializing shared backends; error-handling paths that retry Initialize after a partial failure without cleanup.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- PlayMedia failed: MediaCodecScheduler is null
- PauseMedia failed: MediaCodecScheduler is null
- Unable to read
- unexpected result from audio decoder.DequeueOutputBuffer
- Unable to read
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/fe3b9f88750ef3e9.
Report an issue: GitHub.
Appendix: source
Thrown at sources/engine/Stride.Video/Backends/MediaCodecVideoBackend.cs:42
private MediaCodecVideoExtractor mediaCodecVideoExtractor;
private ImageReader imageReader;
private readonly object imageReaderLock = new();
private IntPtr rgbaScratch;
private int rgbaScratchSize;
private int videoWidth, videoHeight;
private StreamedBufferSound audioSound;
private SoundInstanceStreamedBuffer audioSoundInstance;
private readonly List<AudioEmitterSoundController> audioControllers = new();
public MediaCodecVideoBackend(VideoInstance instance) : base(instance) { }
public override bool UsesHardwareDecode => true;
public override bool Initialize(string url, long startPosition, long length)
{
if (mediaSynchronizer != null || mediaCodecVideoExtractor != null)
throw new InvalidOperationException("mediaCodec has already been initialized");
// Pre-probe dimensions: ImageReader can't be resized, but MediaCodec.Configure already
// needs its Surface, so we have to know width/height upfront. Cheap to open a second
// MediaExtractor here and pull the video format.
(videoWidth, videoHeight) = ProbeVideoDimensions(url, startPosition, length);
// CPU YUV path. The zero-copy alternative Texture.NewFromAndroidHardwareBuffer
// (Stride.Graphics) needs immutable VkSamplerYcbcrConversion descriptor bindings
// that Stride's effect system doesn't yet expose.
// No OnImageAvailable listener: it doesn't fire reliably under the emulator's gfxstream
// BufferQueue, so Update() pulls frames with AcquireLatestImage every tick instead.
// maxImages is deliberately generous: on a cold/janky start the decoder needs several
// output buffers to dequeue into before the game thread starts draining, otherwise the
// gfxstream BufferQueue starves it and it produces nothing for tens of seconds.
imageReader = ImageReader.NewInstance(videoWidth, videoHeight, ImageFormatType.Yuv420888, maxImages: 8);
rgbaScratchSize = videoWidth * videoHeight * 4;
rgbaScratch = Marshal.AllocHGlobal(rgbaScratchSize);View on GitHub (pinned to 96fad776d2)