FMLiveSwitchCocoaAudioSessionManager Class Reference

Centralised audio session manager that owns a single shared VPIO (Voice Processing I/O) AudioUnit. More...

Instance Methods

(NSException *_Nullable) - registerMediaWithMediaId:isSend:isReceive:
 Registers a media component (source or sink) with the AudioSessionManager. More...
 
(void) - unregisterMediaWithMediaId:
 Unregisters a media component from the AudioSessionManager. More...
 
(void) - setUseVoiceProcessingIO:
 Sets whether to use Voice Processing I/O (VPIO) or standard Remote I/O. More...
 
(BOOL) - useVoiceProcessingIO
 Gets whether Voice Processing I/O is enabled. More...
 
(void) - setBypassVoiceProcessing:
 Sets whether to bypass voice processing in VPIO. More...
 
(BOOL) - bypassVoiceProcessing
 Gets whether voice processing is bypassed. More...
 
(void) - setVoiceProcessingEnableAGC:
 Sets whether Automatic Gain Control (AGC) is enabled in VPIO. More...
 
(BOOL) - voiceProcessingEnableAGC
 Gets whether AGC is enabled. More...
 
(void) - setChannelCount:
 Sets the number of audio channels for VPIO. More...
 
(UInt32) - channelCount
 Gets the configured number of audio channels. More...
 
(AudioUnit _Nullable) - getSharedVPIOUnit
 Gets the shared VPIO (Voice Processing I/O) AudioUnit. More...
 
(BOOL) - registerMicrophoneInputCallback:refCon:mediaId:
 Registers a callback to receive microphone input from the shared VPIO unit. More...
 
(void) - unregisterMicrophoneInputCallbackWithMediaId:
 Unregisters the microphone input callback for a given media ID. More...
 
(BOOL) - registerSpeakerOutputWithMediaId:sampleRate:channelCount:bufferSize:
 Registers a speaker output (sink) with the audio mixer. More...
 
(void) - unregisterSpeakerOutputWithMediaId:
 Unregisters a speaker output (sink) from the audio mixer. More...
 
(BOOL) - writeSpeakerDataWithMediaId:data:length:
 Writes audio data from a sink to the mixer's buffer. More...
 
(void) - updateSsrcForMediaId:ssrc:
 Associates a remote RTP SSRC with a previously registered speaker output (sink). More...
 
(NSArray< NSString * > *) - registeredSpeakerOutputMediaIds
 Returns the media IDs of all currently registered speaker output sinks. More...
 
(BOOL) - isSessionActive
 Checks if the AVAudioSession is currently active. More...
 
(NSString *) - getDiagnostics
 Gets diagnostic information about the current audio session state. More...
 
(NSString *_Nullable) - getTelemetryJson
 Returns a JSON string snapshot of AudioSessionManager state for telemetry. More...
 
(void) - addObserver:
 Registers an observer to receive audio quality events. More...
 
(void) - removeObserver:
 Unregisters a previously registered observer. More...
 
(void) - notifySinkFrameDropEventWithMediaId:consecutive:total:
 Called by AudioUnitSink when it detects frame drops (write buffer full). More...
 
(void) - reportSourceAudioActivityWithMediaId:
 Called by AudioUnitSource when the microphone first produces non-silent audio. More...
 
(void) - reportSourceResamplingWithMediaId:captureRate:outputRate:isResampling:
 Called by AudioUnitSource to record its sample rate conversion (resampling) state. More...
 

Class Methods

(instancetype) + sharedInstance
 Returns the shared singleton instance of the AudioSessionManager. More...
 

Properties

NSStringmicrophoneInputMediaId
 

Detailed Description

Centralised audio session manager that owns a single shared VPIO (Voice Processing I/O) AudioUnit.

@discussion This manager provides a centralised approach to audio handling in WebRTC applications, solving common iOS audio issues including:

  • Volume inconsistencies across different iPhone models (13 vs 14+)
  • Echo cancellation problems when using multiple AudioUnits
  • Audio conflicts when multiple media streams are active

The manager owns a single VPIO AudioUnit that handles:

  • Microphone input with built-in echo cancellation and noise suppression
  • Speaker output with mixing support for multiple remote participants
  • AVAudioSession lifecycle management
  • Interruption handling (phone calls, Siri, etc.)
  • Route change handling (headphones, Bluetooth, etc.)

Architecture:

  1. ONE AudioUnitSource registers its callback to receive microphone input
  2. MULTIPLE AudioUnitSinks register and write audio data to be mixed
  3. AudioSessionManager mixes all sink data and plays through VPIO speaker output
  4. VPIO's echo canceller sees both mic input and speaker output for optimal AEC

Thread Safety:

  • All methods are thread-safe and can be called from any thread
  • Internal state is protected by locks
  • Audio callbacks run on real-time audio thread

Usage Pattern:

  1. Media components call registerMediaWithMediaId when starting
  2. Source calls registerMicrophoneInputCallback to receive mic audio
  3. Sinks call registerSpeakerOutputWithMediaId and writeSpeakerDataWithMediaId
  4. Media components call unregisterMediaWithMediaId when stopping
  5. AudioSessionManager automatically starts/stops VPIO based on active media count

Method Documentation

◆ addObserver:

- (void) addObserver: (id<FMLiveSwitchCocoaAudioSessionManagerObserver>)  observer

Registers an observer to receive audio quality events.

Parameters
observerThe observer to register. Stored as a weak reference.

@discussion Observers are called on the main queue when significant audio events occur. Safe to call from any thread. Registering the same observer twice has no effect.

◆ bypassVoiceProcessing

- (BOOL) bypassVoiceProcessing

Gets whether voice processing is bypassed.

Returns
YES if voice processing is bypassed, NO if enabled.

◆ channelCount

- (UInt32) channelCount

Gets the configured number of audio channels.

Returns
Number of channels: 1 for mono, 2 for stereo.

◆ getDiagnostics

- (NSString *) getDiagnostics

Gets diagnostic information about the current audio session state.

Returns
A formatted string containing diagnostic information.

@discussion This provides detailed information about the audio session for debugging:

  • Number of active media components
  • List of registered media IDs
  • Session active state
  • VPIO initialization state
  • Microphone input registration
  • Speaker output registrations with buffer levels
  • AVAudioSession configuration (category, mode, sample rate)
  • Current audio routes (inputs and outputs)

Example Output:

AudioSessionManager Diagnostics:
- Active medias: 3
- Media IDs: source-abc123, sink-def456, sink-ghi789
- Session Active: YES
- VPIO Initialised: YES
- Microphone Input: source-abc123
- Speaker Outputs: 2
- sink-def456: 48000 Hz, 1 ch, 9600 bytes buffered
- sink-ghi789: 48000 Hz, 1 ch, 7200 bytes buffered
- AVAudioSession Category: AVAudioSessionCategoryPlayAndRecord
- AVAudioSession Mode: AVAudioSessionModeVideoChat
- Sample Rate: 48000 Hz

Thread Safety: Safe to call from any thread.

Example:

NSLog(@"Audio Status:\n%@", diagnostics);

◆ getSharedVPIOUnit

- (AudioUnit) getSharedVPIOUnit

Gets the shared VPIO (Voice Processing I/O) AudioUnit.

Returns
The shared VPIO AudioUnit, or NULL if not initialised.

@discussion The VPIO unit is created lazily when first accessed after AVAudioSession is active. This unit is configured for:

  • Input (microphone) on bus 1
  • Output (speaker) on bus 0
  • 16-bit signed integer (short) samples
  • Sample rate matching AVAudioSession (typically 48kHz on iOS)
  • Mono or stereo based on channelCount setting
  • VPIO or RemoteIO based on useVoiceProcessingIO setting
  • Voice processing bypass based on bypassVoiceProcessing setting
  • AGC based on voiceProcessingEnableAGC setting

The returned AudioUnit is owned by AudioSessionManager - do NOT dispose it. Do NOT call AudioUnitInitialize, AudioOutputUnitStart, or AudioOutputUnitStop on this unit. Do NOT set properties on this unit (format, callbacks, etc.) - use the registration methods instead.

This method is primarily used by AudioUnitSource to call AudioUnitRender for pulling mic data.

Thread Safety: Safe to call from any thread.

Example:

if (vpio) {
// Use for AudioUnitRender calls
AudioUnitRender(vpio, ioActionFlags, inTimeStamp, 1, inNumberFrames, bufferList);
}

◆ getTelemetryJson

- (NSString *_Nullable) getTelemetryJson

Returns a JSON string snapshot of AudioSessionManager state for telemetry.

Returns
A JSON string, or nil if serialization fails.

@discussion Captures a point-in-time snapshot for connection-level telemetry:

  • VPIO configuration (useVoiceProcessingIO, bypassVoiceProcessing, voiceProcessingEnableAGC, channelCount)
  • Session state (isSessionActive)
  • Registration counts (hasMicSource, sinkCount, totalRegisteredMedia)
  • manualAudioSessionManagement (iOS only)
  • sourceHadAudio: YES if any registered source has detected non-silent audio since registration
  • sourceResamplingActive: YES if the source's VPIO hardware rate differs from the output rate
  • sourceCaptureRateHz / sourceOutputRateHz: present only when sourceResamplingActive is YES
  • Full diagnostics string from getDiagnostics

Thread Safety: Safe to call from any thread.

◆ isSessionActive

- (BOOL) isSessionActive

Checks if the AVAudioSession is currently active.

Returns
YES if the audio session is active, NO otherwise.

@discussion The session becomes active when the first media component registers and becomes inactive when the last media component unregisters.

An active session means:

  • AVAudioSession is configured and activated
  • VPIO unit may be running (if media components are using it)
  • Audio can be captured and played

Thread Safety: Safe to call from any thread.

Example:

NSLog(@"Audio session is ready");
} else {
NSLog(@"Audio session is not active - register media first");
}

◆ notifySinkFrameDropEventWithMediaId:consecutive:total:

- (void) notifySinkFrameDropEventWithMediaId: (NSString *)  mediaId
consecutive: (UInt32)  consecutive
total: (UInt64)  total 

Called by AudioUnitSink when it detects frame drops (write buffer full).

Parameters
mediaIdThe sink's media ID.
consecutiveConsecutive frames dropped in the current run.
totalTotal frames dropped by this sink since creation.
Note
The event detail also includes "totalUnderruns" (looked up internally) to help distinguish jitter-induced bursts (underruns precede the drop) from device CPU stress (underruns near zero).

@discussion Dispatches a "sinkFrameDrop" observer event, rate-limited to once per 5 minutes per sink. The caller should only invoke this on the first frame of each new drop run (i.e., when consecutive transitions from 0 to 1).

Thread Safety: Safe to call from any thread.

◆ registeredSpeakerOutputMediaIds

- (NSArray< NSString * > *) registeredSpeakerOutputMediaIds

Returns the media IDs of all currently registered speaker output sinks.

Used by ServerConnectionTelemetry to send asmSsrcMap at trigger time, bypassing [audioStream outputs] which may be empty early in the connection. Thread Safety: Safe to call from any thread.

◆ registerMediaWithMediaId:isSend:isReceive:

- (NSException *) registerMediaWithMediaId: (NSString *)  mediaId
isSend: (BOOL)  isSend
isReceive: (BOOL)  isReceive 

Registers a media component (source or sink) with the AudioSessionManager.

Parameters
mediaIdUnique identifier for this media component (e.g., generated GUID).
isSendYES if this media sends audio (microphone), NO otherwise.
isReceiveYES if this media receives audio (speaker), NO otherwise.
Returns
nil on success, NSException with error details on failure.

@discussion This method must be called before any audio operations for a media component. The AudioSessionManager:

  • Tracks the count of active media components
  • Starts the AVAudioSession when the first media registers
  • Initialises the shared VPIO unit when needed
  • Configures AVAudioSession category, mode, and options for VoIP use

If the media component has neither send nor receive audio, this method does nothing.

Call Order:

  1. registerMediaWithMediaId (first - always required)
  2. registerMicrophoneInputCallback (for sources only)
  3. registerSpeakerOutputWithMediaId (for sinks only)

Thread Safety: Safe to call from any thread.

Example:

registerMediaWithMediaId:@"source-abc123"
isSend:YES
isReceive:NO];
if (error) {
NSLog(@"Failed to register: %@", error.reason);
}

◆ registerMicrophoneInputCallback:refCon:mediaId:

- (BOOL) registerMicrophoneInputCallback: (AURenderCallback)  inputCallback
refCon: (FMLiveSwitchZeroingWeakRef *)  refCon
mediaId: (NSString *)  mediaId 

Registers a callback to receive microphone input from the shared VPIO unit.

Parameters
inputCallbackThe AURenderCallback function to call when mic data is available.
refConUser data pointer to pass to the callback (typically self or weakSelf).
mediaIdThe media ID of the source registering this callback.
Returns
YES on success, NO if another source has already registered or on error.

@discussion Only ONE source can register a microphone input callback at a time. The callback will be invoked on the real-time audio thread when the VPIO unit has captured microphone data.

The callback receives data AFTER echo cancellation and noise suppression have been applied.

Callback Signature:

OSStatus MicrophoneCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)

In your callback, you must call AudioUnitRender on the shared VPIO unit to actually pull the microphone data:

AudioUnitRender(vpio, ioActionFlags, inTimeStamp, 1, inNumberFrames, bufferList);

Thread Safety: Safe to call from any thread, but callback runs on real-time audio thread.

Example:

static OSStatus MyMicCallback(void *inRefCon, ...) {
MySource *source = (__bridge MySource *)inRefCon;
return [source handleMicrophoneInput:...];
}
registerMicrophoneInputCallback:MyMicCallback
refCon:(__bridge void *)self
mediaId:_audioSessionId];

◆ registerSpeakerOutputWithMediaId:sampleRate:channelCount:bufferSize:

- (BOOL) registerSpeakerOutputWithMediaId: (NSString *)  mediaId
sampleRate: (Float64)  sampleRate
channelCount: (UInt32)  channelCount
bufferSize: (UInt32)  bufferSize 

Registers a speaker output (sink) with the audio mixer.

Parameters
mediaIdUnique identifier for this sink.
sampleRateSample rate of the audio data this sink will write (e.g., 48000).
channelCountNumber of audio channels (1 for mono, 2 for stereo).
bufferSizeSize in bytes of the circular buffer to allocate for this sink.
Returns
YES on success, NO if already registered or on error.

@discussion This registers a sink with the AudioSessionManager's mixer. A circular buffer is allocated for this sink to store audio data before playback.

Buffer Size Recommendations:

  • For 48kHz mono: 48000 * 1 * 2 bytes * (latency_seconds)
  • Example: 200ms latency = 48000 * 1 * 2 * 0.2 = 19,200 bytes
  • Typical range: 9,600 bytes (100ms) to 38,400 bytes (400ms)

After registering, the sink should call writeSpeakerDataWithMediaId to provide audio data.

The mixer will combine audio from all registered sinks and play through the VPIO speaker output.

Thread Safety: Safe to call from any thread.

Example:

// Register for 200ms of buffering at 48kHz mono
UInt32 bufferSize = 48000 * 1 * sizeof(short) * 0.2;
registerSpeakerOutputWithMediaId:_audioSessionId
sampleRate:48000
bufferSize:bufferSize];

◆ removeObserver:

- (void) removeObserver: (id<FMLiveSwitchCocoaAudioSessionManagerObserver>)  observer

Unregisters a previously registered observer.

Parameters
observerThe observer to remove.

@discussion Safe to call from any thread. No-op if the observer is not registered.

◆ reportSourceAudioActivityWithMediaId:

- (void) reportSourceAudioActivityWithMediaId: (NSString *)  mediaId

Called by AudioUnitSource when the microphone first produces non-silent audio.

Parameters
mediaIdThe source's media ID.

@discussion Sets an internal flag that is included as "sourceHadAudio" in all subsequent observer event detail dictionaries and in getTelemetryJson. Idempotent — safe to call multiple times; only the first call has any effect.

Thread Safety: Safe to call from any thread.

◆ reportSourceResamplingWithMediaId:captureRate:outputRate:isResampling:

- (void) reportSourceResamplingWithMediaId: (NSString *)  mediaId
captureRate: (int)  captureRate
outputRate: (int)  outputRate
isResampling: (BOOL)  isResampling 

Called by AudioUnitSource to record its sample rate conversion (resampling) state.

Parameters
mediaIdThe source's media ID.
captureRateThe hardware capture rate (e.g., 16000 for Bluetooth SCO).
outputRateThe pipeline output rate (e.g., 48000).
isResamplingYES if an AudioConverter is active to bridge the two rates.

@discussion Stored and included in getTelemetryJson as: "sourceResamplingActive": bool "sourceCaptureRateHz": int (only when isResampling is YES) "sourceOutputRateHz": int (only when isResampling is YES) Should be called after configureVPIOFormatAndConverter and again after recovery if rates change.

Thread Safety: Safe to call from any thread.

◆ setBypassVoiceProcessing:

- (void) setBypassVoiceProcessing: (BOOL)  bypassVoiceProcessing

Sets whether to bypass voice processing in VPIO.

Parameters
bypassVoiceProcessingYES to bypass voice processing, NO to enable it.

@discussion When bypassed, VPIO acts more like RemoteIO but maintains the same I/O structure. This disables echo cancellation and noise suppression while keeping the VPIO AudioUnit.

Default: NO (voice processing enabled)

Use Cases:

  • YES: Debug/testing, comparing with/without voice processing
  • NO: Normal VoIP operation (default - recommended)

IMPORTANT: Only effective when useVoiceProcessingIO is YES. This property can be set before or after VPIO initialization.

Thread Safety: Safe to call from any thread.

Example:

// Temporarily bypass voice processing for testing
[[FMLiveSwitchCocoaAudioSessionManager sharedInstance] setBypassVoiceProcessing:YES];

◆ setChannelCount:

- (void) setChannelCount: (UInt32)  channelCount

Sets the number of audio channels for VPIO.

Parameters
channelCountNumber of channels: 1 for mono, 2 for stereo.

@discussion This configures both microphone input and speaker output channel count.

Default: 1 (mono)

Channel Count Guidelines:

  • 1 (Mono): VoIP calls, voice chat (default - recommended for echo cancellation)
  • 2 (Stereo): Music playback, high-quality audio

IMPORTANT:

  • This property can only be set before the VPIO unit is initialised.
  • Stereo mode may affect echo cancellation performance in VPIO.
  • Most VoIP applications should use mono for best AEC results.
  • Attempting to change after VPIO initialization will log a warning and have no effect.

Thread Safety: Safe to call from any thread.

Example:

// Enable stereo for music playback

◆ setUseVoiceProcessingIO:

- (void) setUseVoiceProcessingIO: (BOOL)  useVoiceProcessingIO

Sets whether to use Voice Processing I/O (VPIO) or standard Remote I/O.

Parameters
useVoiceProcessingIOYES to use VPIO (with echo cancellation), NO to use RemoteIO.

@discussion VPIO provides built-in echo cancellation, noise suppression, and automatic gain control. This setting must be configured BEFORE any media registers.

Default: YES (VPIO enabled)

Use Cases:

  • YES: VoIP calls, video conferencing (default - recommended)
  • NO: Music/audio apps where voice processing would degrade quality

IMPORTANT: This property can only be set before the VPIO unit is initialised. Attempting to change it after VPIO initialization will log a warning and have no effect.

Thread Safety: Safe to call from any thread.

Example:

// Disable VPIO for high-quality audio recording

◆ setVoiceProcessingEnableAGC:

- (void) setVoiceProcessingEnableAGC: (BOOL)  enableAGC

Sets whether Automatic Gain Control (AGC) is enabled in VPIO.

Parameters
enableAGCYES to enable AGC, NO to disable it.

@discussion AGC automatically adjusts microphone gain to maintain consistent audio levels. When enabled, quiet speech is amplified and loud speech is attenuated.

Default: YES (AGC enabled)

Use Cases:

  • YES: Normal VoIP calls (default - recommended for consistent volume)
  • NO: Professional recording, when manual gain control is preferred

IMPORTANT: Only effective when useVoiceProcessingIO is YES and bypassVoiceProcessing is NO. This property can be set before or after VPIO initialization.

Thread Safety: Safe to call from any thread.

Example:

// Disable AGC for manual volume control
[[FMLiveSwitchCocoaAudioSessionManager sharedInstance] setVoiceProcessingEnableAGC:NO];

◆ sharedInstance

+ (instancetype) sharedInstance

Returns the shared singleton instance of the AudioSessionManager.

Returns
The shared AudioSessionManager instance.

@discussion This is the primary way to access the AudioSessionManager. The instance is created lazily on first access and persists for the lifetime of the application.

◆ unregisterMediaWithMediaId:

- (void) unregisterMediaWithMediaId: (NSString *)  mediaId

Unregisters a media component from the AudioSessionManager.

Parameters
mediaIdThe unique identifier of the media component to unregister.

@discussion This method should be called when a media component is being destroyed. The AudioSessionManager:

  • Removes the media from its tracking list
  • Unregisters any callbacks associated with this media
  • Stops the AVAudioSession when the last media unregisters
  • Cleans up the shared VPIO unit when no longer needed

If the mediaId was not registered, this method does nothing.

Thread Safety: Safe to call from any thread.

Example:

unregisterMediaWithMediaId:@"source-abc123"];

◆ unregisterMicrophoneInputCallbackWithMediaId:

- (void) unregisterMicrophoneInputCallbackWithMediaId: (NSString *)  mediaId

Unregisters the microphone input callback for a given media ID.

Parameters
mediaIdThe media ID of the source that registered the callback.

@discussion This removes the microphone callback registration. After calling this, the source will no longer receive microphone data.

If the provided mediaId does not match the currently registered callback, this method does nothing.

Thread Safety: Safe to call from any thread.

Example:

unregisterMicrophoneInputCallbackWithMediaId:_audioSessionId];

◆ unregisterSpeakerOutputWithMediaId:

- (void) unregisterSpeakerOutputWithMediaId: (NSString *)  mediaId

Unregisters a speaker output (sink) from the audio mixer.

Parameters
mediaIdThe unique identifier of the sink to unregister.

@discussion This removes the sink from the mixer and frees its circular buffer. After calling this, the sink can no longer write audio data.

If the provided mediaId was not registered, this method does nothing.

Thread Safety: Safe to call from any thread.

Example:

unregisterSpeakerOutputWithMediaId:_audioSessionId];

◆ updateSsrcForMediaId:ssrc:

- (void) updateSsrcForMediaId: (NSString *)  mediaId
ssrc: (UInt32)  ssrc 

Associates a remote RTP SSRC with a previously registered speaker output (sink).

Parameters
mediaIdThe unique identifier of the sink (as returned during registration).
ssrcThe 32-bit SSRC of the remote RTP stream feeding this sink.

@discussion The SSRC is not available at sink creation time — it is known from SDP negotiation, which completes after the sink is registered. Call this method once the SSRC is determined (e.g. from the audio stream's RTP receiver after connection setup).

The SSRC is included in subsequent getDiagnostics and getTelemetryJson output so that the server can correlate sink media IDs with remote participants.

If mediaId is not registered, this method does nothing. Thread Safety: Safe to call from any thread.

◆ useVoiceProcessingIO

- (BOOL) useVoiceProcessingIO

Gets whether Voice Processing I/O is enabled.

Returns
YES if VPIO is enabled, NO if using RemoteIO.

◆ voiceProcessingEnableAGC

- (BOOL) voiceProcessingEnableAGC

Gets whether AGC is enabled.

Returns
YES if AGC is enabled, NO if disabled.

◆ writeSpeakerDataWithMediaId:data:length:

- (BOOL) writeSpeakerDataWithMediaId: (NSString *)  mediaId
data: (const void *)  data
length: (UInt32)  length 

Writes audio data from a sink to the mixer's buffer.

Parameters
mediaIdThe unique identifier of the sink writing data.
dataPointer to audio data in 16-bit signed integer (short) format.
lengthLength of data in bytes.
Returns
YES on success, NO if buffer is full or sink not registered.

@discussion This writes audio data to the sink's circular buffer in the mixer. The data will be mixed with other sinks and played through the VPIO speaker output.

Data Format Requirements:

  • Must be 16-bit signed integer (short) samples
  • Must match the channelCount specified during registration
  • Must be in native endian format
  • Interleaved if stereo (L, R, L, R, ...)

Buffer Full Handling:

  • If the circular buffer is full, this method returns NO
  • The audio frame is dropped
  • This typically happens if the sink is writing faster than real-time
  • Consider reducing write frequency or increasing buffer size

Thread Safety: Safe to call from any thread, typically called from media decode thread.

Example:

// Assuming we have decoded audio in 16-bit signed format
short *audioSamples = ...; // 480 samples (10ms at 48kHz)
int length = 480 * sizeof(short); // 960 bytes
writeSpeakerDataWithMediaId:_audioSessionId
data:audioSamples
length:length];
if (!success) {
NSLog(@"Buffer full - dropping audio frame");
}

Property Documentation

◆ microphoneInputMediaId

- (NSString*) microphoneInputMediaId
readnonatomicassign