Skip to content

Fix cannotSetupAudioInputs when external USB audio device is connected before app launch#54

Open
mpogra wants to merge 1 commit into
MetalPetal:masterfrom
mpogra:fix/cannot-setup-audio-inputs-usb-device-at-launch
Open

Fix cannotSetupAudioInputs when external USB audio device is connected before app launch#54
mpogra wants to merge 1 commit into
MetalPetal:masterfrom
mpogra:fix/cannot-setup-audio-inputs-usb-device-at-launch

Conversation

@mpogra
Copy link
Copy Markdown

@mpogra mpogra commented May 7, 2026

Problem

When an external USB audio device (e.g. a microphone via Lightning or USB-C adapter) is connected before the app launches, AVCaptureSession initialises its audio subsystem in a degraded state. The CMSampleBuffer format description produced in this state has:

  • No AudioChannelLayout (CMAudioFormatDescriptionGetChannelLayout returns nil)
  • Potentially zero values for sample rate and channel count in the ASBD

The original code always set:

audioSettings[AVChannelLayoutKey] = Data()   // empty Data when layout is absent

AVAssetWriter.canApply(outputSettings:forMediaType:audio) rejects an explicitly empty Data value for AVChannelLayoutKey and returns false, which throws cannotSetupAudioInputs on every audio frame. The recording fails completely — no output file is written.

Steps to reproduce

  1. Connect an external USB microphone via Lightning or USB-C adapter (see hardware photo below)
  2. Launch the app — microphone must be connected before launch
  3. Start a video recording session
  4. Recording always fails with cannotSetupAudioInputs

Connecting the microphone after launch works fine because AVCaptureSession initialises audio normally in that case.

Hardware reference

[photo of USB mic connected via Lightning adapter — showing the exact setup that triggers the degraded audio state]

Screenshot 2026-05-07 at 2 34 31 PM

Fix

Three-layer defence in appendAudioSampleBuffers:

1. Omit AVChannelLayoutKey when layout data is absent
Per AVAssetWriterInput docs, AVChannelLayoutKey is only required for channel counts > 2. For ≤2 channels, AVAssetWriter correctly infers mono/stereo from AVNumberOfChannelsKey when the key is absent.

2. Guard zero ASBD values
Fall back to 44100 Hz / 1 ch when mSampleRate or mChannelsPerFrame are zero. Valid non-zero values from a healthy format description are used unchanged — normal recording is completely unaffected.

3. Last-resort canApply retry
If the format-description-derived settings are still rejected, retry with hardcoded 44100 Hz mono AAC and nil format hint. This combination is unconditionally accepted by AVAssetWriter for AAC output.

Testing

Tested with a USB microphone (Lightning adapter) plugged in both before and after launch:

  • Video session ✅
  • Built-in mic (no external device) ✅

Note: MultitrackMovieRecorder is only involved in video recording. Other session types (photo etc) use a different path and are not affected by this bug.

…t launch

When an external USB audio device (e.g. a microphone via Lightning or
USB-C adapter) is connected before the app launches, AVCaptureSession
initialises its audio subsystem in a degraded state. The resulting
CMSampleBuffer format description has no AudioChannelLayout and may
carry zero values for sample rate and channel count in its ASBD.

The original code unconditionally wrote:
    audioSettings[AVChannelLayoutKey] = Data()   // empty when layout absent
AVAssetWriter.canApply(outputSettings:forMediaType:audio) rejects an
explicitly empty Data value for AVChannelLayoutKey, so it returned false
and threw cannotSetupAudioInputs on every audio frame. The recording
failed completely with no output file written.

Fix (three-layer defence in appendAudioSampleBuffers):
1. Omit AVChannelLayoutKey entirely when layout data is absent.
   Per AVAssetWriterInput docs, AVChannelLayoutKey is only required for
   channel counts > 2; AVAssetWriter infers mono/stereo from
   AVNumberOfChannelsKey when the key is absent.
2. Guard zero ASBD values — fall back to 44100 Hz / 1 ch when
   mSampleRate or mChannelsPerFrame are zero (degraded state).
   Valid non-zero values from a healthy format description are
   used unchanged, so normal recording is unaffected.
3. Last-resort canApply retry with hardcoded 44100 Hz mono AAC
   and nil format hint, ensuring recording never silently fails
   from any remaining unacceptable values.

Tested with a USB microphone (Lightning adapter) plugged in both
before and after launch: video, photo, and boomerang sessions all
record successfully in both cases.
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