stride3d/stride · error · InvalidOperationException

AVAssetReader.StartReading (audio) failed

Error message

AVAssetReader.StartReading (audio) failed: {(err != null ? err.LocalizedDescription : "unknown")}

What it means

StreamedBufferSoundSource (AVFoundation backend) throws this when AVAssetReader.StartReading() fails after the audio output has been attached. The reader cannot begin producing decoded audio frames, so streaming cannot proceed. The AVAssetReader.Error LocalizedDescription is embedded to reveal the underlying cause (format mismatch, corrupted asset, expired track, etc.).

Solutions

  1. Inspect the embedded LocalizedDescription in the exception message for the concrete AVFoundation reason
  2. Verify the source file is decodable (playable in AVPlayer / afinfo) before creating the reader
  3. Check that the reader output settings (outputSettings dict on AVAssetReaderAudioMixOutput) request a supported format like Mono16 or Stereo16 PCM
  4. Re-create the reader (DisposeReader + re-init) after seeking instead of reusing a reader whose StartReading already failed
  5. Ensure the asset still has an audio track (check track.Count > 0 and mediaType == AVMediaType.Audio) before adding the output

Example fix

// before
var settings = new AVAudioSettingsFloat { AVFormatIDKey = (int)AVAudioFormat.ThreeSixty }; // unsupported
audioReader.AddOutput(new AVAssetReaderAudioMixOutput(new[] { audioTrack }, settings));
audioReader.StartReading();
// after
var settings = new AVAudioSettingsFloat
{
    AVFormatIDKey = (int)CMAudioFormatID.LinearPCM,
    AVLinearPCMBitDepthKey = 16,
    AVLinearPCMIsFloatKey = false,
    AVLinearPCMIsBigEndianKey = false,
    AVNumberOfChannelsKey = 1
};
var output = new AVAssetReaderAudioMixOutput(new[] { audioTrack }, settings);
audioReader.AddOutput(output);
if (!audioReader.StartReading())
    Logger.Error($"StartReading failed: {audioReader.Error?.LocalizedDescription}");
Defensive patterns

Strategy: try-catch

Validate before calling

// AVFoundation/macOS
if (audioTrack == null || asset.Tracks.Length == 0)
    throw new InvalidOperationException("Asset has no audio track");
var playable = asset.IsPlayable;
if (!playable) throw new InvalidOperationException("Asset not playable");

Type guard

bool HasReadableAudioTrack(AVAssetReader reader) => reader != null && reader.Status == AVAssetReaderStatus.Unknown && reader.Error == null;

Try / catch

try
{
    source = new StreamedBufferSoundSource(path, services);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("StartReading"))
{
    Logger.Error($"Audio reader init failed: {ex.Message}");
    source = null; // fall back to non-streamed sound
}

Prevention

When it happens

Trigger: Calling InitializeMediaExtractor or SeekInternalImpl on macOS/iOS builds with an audio file whose audioTrack was invalidated or whose AVAssetReaderAudioMixOutput settings (AVFormatIDKey/AVNumberOfChannelsKey) are unsupported by the decoder; StartReading returns false and the exception is thrown.

Common situations: Loading an audio file with a sample-rate/channel-count conversion AVFoundation cannot perform; reading a DRM-protected or corrupt media file; seeking on a disposed/re-created reader after the asset track was released; wrong outputSettings keys passed to the reader.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/0d82ef3fac768a99. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Audio/StreamedBufferSoundSource.AVFoundation.cs:118

            audioReader.TimeRange = new CMTimeRange { Start = startCMTime, Duration = CMTime.PositiveInfinity };
        }

        // 16-bit signed LE interleaved PCM — the format Stride.Audio consumes downstream.
        var settings = new NSMutableDictionary
        {
            [AVAudioSettings.AVFormatIDKey] = NSNumber.FromUInt32((uint)AudioFormatType.LinearPCM),
            [AVAudioSettings.AVLinearPCMBitDepthKey] = NSNumber.FromInt32(16),
            [AVAudioSettings.AVLinearPCMIsBigEndianKey] = NSNumber.FromBoolean(false),
            [AVAudioSettings.AVLinearPCMIsFloatKey] = NSNumber.FromBoolean(false),
            [AVAudioSettings.AVLinearPCMIsNonInterleaved] = NSNumber.FromBoolean(false),
        };

        audioOutput = new AVAssetReaderAudioMixOutput(new[] { audioTrack }, settings);
        audioReader.AddOutput(audioOutput);
        if (!audioReader.StartReading())
        {
            var err = audioReader.Error;
            throw new InvalidOperationException(
                $"AVAssetReader.StartReading (audio) failed: {(err != null ? err.LocalizedDescription : "unknown")}");
        }
    }

    private void DisposeReader()
    {
        audioOutput?.Dispose();
        audioOutput = null;
        audioReader?.Dispose();
        audioReader = null;
    }

    private bool ExtractSomeAudioData(out bool endOfFile)
    {
        endOfFile = audioExtractionDone;
        if (audioExtractionDone || audioOutput == null)
            return false;

View on GitHub (pinned to 96fad776d2)