Dive into the technical aspects of audio on your device, including codecs, format support, and customization options.

Audio Documentation

Posts under Audio subtopic

Post

Replies

Boosts

Views

Activity

Video recording goes fine but adding audio fails mysteriously
I'm trying to update an old unity app for a client. The app has been crashing on iOS in a plugin they use called NatCorder. They use it to record video only separately and then re-record it with effects and audio gathered separately. Instead of trying to update the plugin to something else which would be quite the hassle, I noticed the API for the native part of the plugin, where the crash occurs, is very simple, especially if you don't try to support everything the plugin does and the app does not use. So I tried to re-implement that native library using AVFoundation. I got the video recording right, it captures the camera from the iPhone and writes it to a file properly. However, when the app does the second part, where it sends video and audio frames to the plugin, it fails. The app sends all the video frames and then sends all the audio frames. The video frames are eaten fine by AVFoundation but the audio fails at random points with unknown errors. I wonder if I'm trying to use incompatible audio-video formats or if I'm using timestamps wrong or something. Here's my init code. Anything suspicious to you? void* NCCreateMP4Recorder(int width, int height, float framerate, int bitrate, int keyframeInterval, int sampleRate, int channelCount, const char* recordingPath, void (*callback)(void*, void*), void* context) { Recorder* recorder = calloc(1, sizeof(Recorder)); recorder->context = context; recorder->callback = callback; recorder->path = strdup(recordingPath); recorder->width = width; recorder->channelCount = channelCount; recorder->sampleRate = sampleRate; recorder->height = height; NSError *error = nil; NSURL* url = createURLFromArgumentCString(recordingPath); recorder->writer = [AVAssetWriter assetWriterWithURL:url fileType:AVFileTypeMPEG4 error:&error]; if (recorder->writer == nil) NSLog(@"Failed creating media writer: %@", error); NSDictionary *videoSettings = @{ AVVideoCodecKey: AVVideoCodecTypeH264, AVVideoWidthKey: @(width), AVVideoHeightKey: @(height), AVVideoCompressionPropertiesKey: @{ AVVideoAverageBitRateKey: @(bitrate), AVVideoMaxKeyFrameIntervalKey: @(keyframeInterval), } }; recorder->video = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings]; if (recorder->video == nil) NSLog(@"Failed creating video writer input"); recorder->video.expectsMediaDataInRealTime = true; NSDictionary* videoSource = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey, [NSNumber numberWithInt:width], kCVPixelBufferWidthKey, [NSNumber numberWithInt:height], kCVPixelBufferHeightKey, nil]; recorder->videoAdaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:recorder->video sourcePixelBufferAttributes:videoSource]; if (recorder->videoAdaptor == nil) NSLog(@"Failed creating video adaptor"); if ([recorder->writer canAddInput:recorder->video]) [recorder->writer addInput:recorder->video]; else NSLog(@"Could not add video input to writer"); if (sampleRate > 0 && channelCount > 0) { AudioChannelLayout layout = { .mChannelLayoutTag = channelCount == 1 ? kAudioChannelLayoutTag_Mono : kAudioChannelLayoutTag_Stereo, .mChannelBitmap = 0, .mNumberChannelDescriptions = 0 }; NSDictionary* audioOutputSettings = @{ AVFormatIDKey: @(kAudioFormatMPEG4AAC), AVNumberOfChannelsKey: @(channelCount), AVSampleRateKey: @(sampleRate), AVEncoderBitRateKey: @128000, AVChannelLayoutKey: [NSData dataWithBytes:&layout length:sizeof(layout)] }; recorder->audio = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:audioOutputSettings]; if (!recorder->audio) NSLog(@"Failed creating audio adaptor"); recorder->audio.expectsMediaDataInRealTime = true; AudioStreamBasicDescription audioStreamDesc = { .mSampleRate = sampleRate, .mFormatID = kAudioFormatLinearPCM, .mFormatFlags = kAudioFormatFlagIsPacked | kAudioFormatFlagIsFloat, .mBytesPerPacket = channelCount * sizeof(float), .mFramesPerPacket = 1, .mBytesPerFrame = channelCount * sizeof(float), .mChannelsPerFrame = channelCount, .mBitsPerChannel = sizeof(float) * 8, }; OSStatus status = CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &audioStreamDesc, sizeof(layout), &layout, 0, nil, nil, &recorder->audioDesc); if (status) NSLog(@"Failed creating audio format description: %d", (int)status); if ([recorder->writer canAddInput:recorder->audio]) [recorder->writer addInput:recorder->audio]; else NSLog(@"Could not add audio input to writer"); } if (![recorder->writer startWriting]) NSLog(@"Could not start writing: %@", recorder->writer.error); [recorder->writer startSessionAtSourceTime:kCMTimeZero]; NSLog(@"Recording started to %s", recordingPath); return recorder; }
2
0
346
Aug ’26
Processing / tapping an HLS audio stream (or global app output)
I'm trying to do some realtime audio processing on audio served from an HLS stream (i.e. an AVPlayer created using an M3U HTTP URL). It doesn't seem like attaching an AVAudioMix configured with with an `audioTapProcessor` has any effect; none of the callbacks except `init` are being invoked. Is this a known limitation? If so, is this documented somewhere?If the above is a limitation, what are my options using some of the other audio APIs? I looked into `AVAudioEngine` as well but it doesn't seem like there's any way I can configure any of the input node types to use an HLS stream. Am I wrong? Are there lower level APIs available to play HLS streams that provide the necessary hooks?Alternatively, is there some generic way to tap into all audio being output by my app regardless of its source?Thanks a lot!
12
0
5.5k
Aug ’26
Preserve AirPods Transparency while capturing one AirPod microphone for low latency cross ear audio
I am prototyping an iOS accessibility audio application for people with unilateral hearing loss. The goal is simple. Capture environmental audio using the AirPod on the poor hearing side and route that audio with very low latency into the AirPod on the working hearing side. The core concept is already working on physical AirPods and an iPhone. Our desired signal path is Right AirPod microphone → iPhone audio processing → Left AirPod output The user sets the AirPods microphone in Settings to Always Right AirPod. The app then captures that Bluetooth microphone and routes the signal into the left output channel. Using AVAudioSession and AVAudioEngine we have successfully achieved low latency cross ear audio. The main issue is native AirPods Transparency. When the app activates the AirPods Bluetooth microphone using HFP, the transferred audio continues working correctly, but native Transparency audio on the receiving AirPod effectively disappears. Interestingly, iOS still reports Transparency as enabled in Control Center and AirPods settings. This means the user can hear audio transferred from the poor hearing side, but their working ear loses much of the normal environmental sound that Transparency previously provided. We have tested several configurations. Normal Bluetooth HFP with playAndRecord This provides the best result so far. The AirPods microphone signal is strong. The output route exposes two channels. We can route the microphone signal only to the opposite AirPod. Latency can be reduced enough that conversation feels nearly real time. However, native Transparency on the receiving AirPod effectively stops providing environmental audio. bluetoothHighQualityRecording Microphone quality is noticeably better. However, latency is significantly higher and produces an audible delayed or echo effect during conversation. Transparency has the same general issue. multiRoute with dualRoute This allows the iPhone audio hardware and AirPods to operate simultaneously. It appears to preserve more ambient awareness on the working side. However, the AirPods microphone becomes substantially weaker when used as the secondary Bluetooth HFP input. We measured the incoming AirPods microphone signal at roughly minus 40 dB during normal speech testing. Adding 12 to 18 dB of software gain increases distortion without materially improving intelligibility. farFieldInput with dualRoute We tested the raw AirPods microphone with and without farFieldInput. The microphone levels and practical pickup were very similar. Far field speech processing improved intelligibility somewhat, but did not solve the weak secondary Bluetooth input signal. Our main technical question is Is there a supported AVAudioSession configuration, Core Audio API, AirPods API, or entitlement that allows a third party application to capture an AirPods microphone while preserving the AirPods native Transparency processing on the output AirPod? Ideally we want Right AirPod microphone capture plus native Transparency on the left AirPod plus application generated right to left audio mixed into the left AirPod all operating simultaneously. We do not need access to Apple's Transparency microphone signal itself if native Transparency can simply remain active while our application audio is mixed into the receiving AirPod. If this is not currently possible using public APIs, is this an intentional platform limitation of the AirPods Bluetooth microphone route? We would also appreciate guidance on whether there is another recommended architecture for this accessibility use case. The low latency cross ear routing itself works surprisingly well. Preserving natural environmental audio in the user's working ear while the Bluetooth microphone is active is currently the main technical blocker. Testing has been performed on a physical iPhone running iOS 26.x using Xcode 26.6 and current AirPods hardware, not the Simulator. Thank you for any guidance.
0
0
316
Aug ’26
watchOS: Network framework WebSocket loses its path ~35 s in, while URLSession keeps working
Following up on TN3135 and the resolution in https://developer.apple.com/forums/thread/773362 — that thread solved establishing a low-level connection on watchOS (the asynchronous AVAudioSession.activate(options:completionHandler:) instead of the synchronous setActive()). This question is about a connection staying established, which I could not find discussed anywhere. Environment: Apple Watch, watchOS 26.6 (23U67). Audio app, WKBackgroundModes = ["self-care"]. Real device, TestFlight build, not the simulator. What works Opening an NWConnection WebSocket to my own server is reliable — 8 attempts out of 8 reached .ready in 0.28–0.98 s, and an echo frame round-tripped in 27–89 ms. Interestingly, in my measurements it opens under BOTH activation variants: the asynchronous activate(options:completionHandler:) AND the synchronous setActive(true). The two are within ~0.2 s of each other. I mention it only because the thread above concluded the synchronous one is insufficient; on 26.6 I cannot reproduce that difference for establishment. What fails The connection goes quiet after roughly half a minute, and an NWPathMonitor running alongside it shows why: the path transitions to .unsatisfied. Four runs: +34.0 s (cellular) +34.6 s (cellular) +36.0 s (Wi-Fi) +34.3 s (companion link only — availableInterfaces ["other", "other"]) The server sends a heartbeat frame every 5 s and closes the socket on a schedule, so I can tell "the peer closed" from "we stopped receiving". The client receives beats 1–6 (5 s … 30 s) and then nothing; the scheduled close never arrives. What I ruled out Server side. The same client construction run on macOS against the same endpoint receives all 8 heartbeats and the scheduled close at 45.1 s. Both audio-session activation variants — no difference, as above. Network type — cellular, Wi-Fi and companion-link-only all drop at ~35 s. The app being suspended. The app keeps logging densely throughout, and in the last run it held a WKExtendedRuntimeSession (delegate reported extendedRuntimeSessionDidStart) and was actively playing audio through AVAudioEngine from the first second — i.e. the audio-streaming condition TN3135 describes — for the entire window. The path dropped anyway, at +34.3 s. An idle socket. Server traffic arrives every 5 s until the drop. The comparison that puzzles me The same app, on the same watch, the same afternoon, relays the same realtime audio session over plain HTTPS (URLSession) instead — and that runs for 64 s continuously without a stall, including straight through a WatchConnectivity "reachability settled: unreachable" transition. So a high-level URLSession request stream survives a period in which a low-level NWConnection's path is reported unsatisfied. That is consistent with the note in thread 773362 that "on watchOS every session is kinda like a background session, where the actual work is done out of process" — but it leaves me unsure what the intended behaviour is. Questions Is a ~35 s path lifetime the expected behaviour for low-level networking on watchOS, or does it indicate something wrong on my side? Does the TN3135 audio-streaming exception cover only the establishment of a low-level connection, or is it also supposed to keep the path available for the duration of the audio streaming? If it is supposed to persist: is there something beyond an active audio session, flowing audio and a WKExtendedRuntimeSession that an app must do to keep the path alive? If ~35 s is the expected ceiling, is a WebSocket a supported transport for a multi-minute conversational audio session on watchOS at all — or is relaying over URLSession the intended approach despite the guidance to prefer Network framework? Happy to file a bug with a sysdiagnose and a reduced sample project if that is more useful — please say the word and I will attach the numbers above.
4
0
1.8k
Aug ’26
Setting appEntityIdentifiers on Now Playing content from a RemoteMediaSessionExtension
I'm using the new RemoteMediaSession API (iOS 27) to surface a remote device's playback (network speakers) on the Lock Screen / Control Center. I'd like to link the presented MusicContent to my App Intents entities so Siri can answer "what's playing?" / "tell me more about this artist," using appEntityIdentifiers. The problem: that property is unavailable in extensions. @available(iOSApplicationExtension, unavailable) extension MediaContentRepresentable { public var appEntityIdentifiers: [EntityIdentifier] { get set } } Result: an extension-hosted remote session seems to have no supported way to attach App Intents entity identifiers to its content. A local MediaSession can set it, but only while the app is running. Questions: Is there a supported way to associate appEntityIdentifiers with RemoteMediaSession content that I'm missing? If not, is this an intentional limitation? I've filed an enhancement request — FB24301827. Any guidance appreciated. Thanks!
0
0
560
Aug ’26
MacOS Music App Returning 404 to Play Next Commands from iTunes Remote app
MacOS Music App No Longer Accepts "Play Next" and "Add to Up Next" from iTunes Remote app. Connect to your library through the iTunes Remote App. Navigate to a song within the iOS iTunes Remote App and press and hold on the song and when the action sheet comes up select "Play Next" or "Add to Up Next". View the MacOS Music App's Playing Next queue to discover that the additions were not made. Thanks for any help you can provide on this. I'd really love to see this working again. I captured network traffic (tcpdump) between the iTunes Remote iOS app and Music.app on macOS 26 (Tahoe). When tapping "Play Next" on a track, the Remote app sends: GET /ctrl-int/1/playqueue-edit?command=add&query='dmap.itemid:27387'&sort=album&mode=3 Music.app responds with HTTP 404 Not Found. "Add to Up Next" sends the same endpoint with mode=0 and also receives 404 Not Found. Other Remote app functions work correctly over the same connection (play/pause, skip), browsing the library, and viewing the queue all return successful responses. Only the queue-add operation returns 404.
0
0
698
Aug ’26
.longFormAudio and USB mic input
I am trying to stream audio from a USB input to a set of AirPlay speakers. I can get this to work to a single AirPlay speaker when I use .playAndRecord and don't use .longFormAudio in the AVAudioSession setup but I hear some audio glitches. I believe these glitches to be audio under-run at the speaker due to differences in clock rates, etc. As I understand the API, to get rid of the audio glitches, I need to use .longFormAudio to enable AirPlay2 and get the speaker to deal with tracking the audio sample timing and have the speaker do any re-sampling when the clocks drift. But if I turn on .longFormAudio, the API will not allow me to use .playAndRecord. Is there a way to get AirPlay2 re-timing behaviors and also enable mic input in the same IOS app?
0
0
670
Aug ’26
Apple-supported alternative to MusicKit JS authorization for child accounts
I’m developing a dedicated children’s audio player using MusicKit JS. Ideally, a child would have access to their own Apple Music library and listening history while remaining managed through Family Sharing. Apple Developer Support confirmed that MusicKit cannot be authorized for an under-13 Apple Account due to age restrictions. Is there an Apple-supported alternative, such as parent authorization with access to a child’s library through any other SDK/API path?
0
0
700
Aug ’26
Why isn’t Audio Output a per-app permission, like Microphone?
iOS already gives users a simple per-app Microphone permission: Settings → Apps → [App] → Microphone: On/Off Why isn’t there an equivalent permission in the other direction? Settings → Apps → [App] → Audio Output: On/Off This would solve a surprisingly common problem: I may deliberately be listening to Spotify, an audiobook or a podcast, then open another app which suddenly produces audio from an advertisement or autoplaying video. That audio may mix with, duck, or even interrupt what I actually chose to listen to. As I understand the current architecture, apps use AVAudioSession to describe how their audio should interact with other audio. But much of that policy is therefore controlled by the application producing the unwanted audio, rather than by the device owner. The simplest solution wouldn’t require a per-app mixer or complicated audio controls. Just one user-controlled permission: Allow Audio Output: On / Off When disabled, iOS would prevent that app from producing audible media output, while audio sessions belonging to other apps would continue normally. Conceptually, this seems very similar to the existing Microphone permission: Microphone: Can this app receive audio from my device? Audio Output: Can this app produce audio on my device? More advanced controls — per-app volume, permission to interrupt other audio, ducking policy, etc. — could potentially come later. But they aren’t necessary to solve the fundamental problem. I’m curious from an AVAudioSession perspective: Is there a technical reason why iOS could not enforce an OS-level per-app Audio Output permission in the same way it already enforces Microphone access? And if there isn’t, would others find this useful?
0
0
560
Aug ’26
Fetch tracks from a playlist
If an app allows people to create a playlist and add more songs to that created playlist, it would make sense to guard them from accidentally adding the same song to the playlist more than once. In this code, even though it is successfully receiving the existing playlist from the request, its tracks and entries always show as nil even when there are songs in the playlist. Any suggestions for how to guard against adding duplicates to a playlist? Thank you! var request = MusicLibraryRequest<Playlist>() request.filter(matching: \.name, equalTo: "AppGeneratedPlaylist") let response = try await request.response() if let existingPlaylist = response.items.first { if let tracks = existingPlaylist.entries, tracks.contains(where: { $0.id == song.id }) { print("Song is already in the playlist, so don't add again") return } else { try await MusicLibrary.shared.add(song, to: existingPlaylist) print("Added song to existing playlist: \(existingPlaylist.name)") print("Count of tracks: \(existingPlaylist.tracks?.count)") print("Count of entries: \(existingPlaylist.entries?.count)") print("Current tracks: \(existingPlaylist.tracks?.map(\.id))") print("Current entries: \(existingPlaylist.entries?.map(\.id))") } }
1
0
1.1k
Aug ’26
Native WebRTC remote audio stops after ~1 hour while Safari still plays the same stream
Hello, I am developing BROXMEDIA Intercom, an iOS intercom application for live audiovisual production. The app uses a native Swift audio plugin, Google WebRTC, AVAudioSession, and a Capacitor user interface. The current TestFlight version is 2.0, build 17. Environment: iPhone 16 Pro Max iOS 26.5.2 TestFlight internal build AVAudioSession category: playAndRecord AVAudioSession mode: voiceChat Background audio capability enabled Bidirectional WebRTC audio between a web browser and the native iOS app Observed behavior: A remote web browser publishes WebRTC audio. The native iOS app receives and plays the audio correctly for approximately one hour. Wi-Fi disconnection/reconnection and airplane mode on/off initially recover correctly. After the prolonged session, the native app stops playing the remote audio. Signaling and participant presence remain connected. The remote participant is still shown as speaking. Completely closing and reopening the native app does not recover the remote audio. Safari on the same iPhone, connected to the same room and network, can still hear the same remote transmission. Restarting the remote web publication usually causes the native app to receive audio again. This suggests that the remote publication, network connection, signaling server, and device audio hardware are still operational when the native route fails. We are investigating whether: AVAudioSession or the underlying WebRTC audio unit has stopped rendering; the native RTCPeerConnection retains a stale receiver or audio track; inbound RTP has stopped even though the peer remains connected; an interruption, route change, or media-services reset has not been fully recovered. Our current recovery logic checks the peer connection state and whether a remote audio track object exists. However, we do not yet continuously verify that inbound RTP packets or bytes are increasing for each participant. Questions: Can AVAudioSession or its underlying audio unit stop rendering audio while RTCPeerConnection signaling remains connected? Which AVAudioSession or audio-unit callbacks should be monitored to distinguish an iOS audio-session failure from a WebRTC receiver or inbound-RTP failure? After AVAudioSession.mediaServicesWereResetNotification, should an app recreate the complete WebRTC audio engine, or is reactivating AVAudioSession normally sufficient? Is monitoring inbound RTP progression and audio energy the recommended way to detect a remote audio track that still exists but is no longer delivering usable audio? Are there any known considerations for prolonged bidirectional VoIP-style audio using playAndRecord, voiceChat, and background audio? We can add diagnostic logging and provide a Feedback Assistant report with sysdiagnose if the problem is reproduced again. Thank you.
0
0
441
Aug ’26
VPIO Audio Ducking
Hey y'all, I'm new here and in the process of building some audio software. I'm hitting a roadblock trying to incorporate VPIO into audio playback states without it ducking what's currently playing in my DAW. I have a few questions: Can VPIO other-audio ducking be completely disabled on macOS, beyond fixed/minimum? Minimum is not enough for professional audio environments. Is attenuation of an unrelated application routed through a different Core Audio output device expected? Is split-device VPIO—built-in microphone input with Apollo/Universal Audio output—supported? Why can AVAudioEngine.start() succeed while the engine remains stopped and immediately emits a configuration-change notification? Thanks for any insight into this!
0
0
1.2k
Jul ’26
AVRoutePickerView's presented route list is automatically dismissed when CXProvider reports the call as connected (`reportOutgoingCall(with:connectedAt:)`)
Hi, I want my user to be able to change their audio output device while calling someone, even before the call is established. But I've run into an issue combining CallKit and AVRoutePickerView. On iOS 26 (Xcode 26.5, tested on iPhone 13 Mini): when my AVRoutePickerView's route list is presented while my call is still connecting (not yet established), the presented list is automatically dismissed the moment the app reports the call as connected via CXProvider.reportOutgoingCall(with:connectedAt:). That doesn't seem natural to me — it should stay open, since the user is actively interacting with unrelated system UI at that moment. This happens even when nothing about the AVAudioSession itself changes: no category change, no route change, no didActivate/didDeactivate callback fires around that time. The dismissal is caused by the CallKit "connected" state transition itself, but I don't know why, or whether it's intentional. Steps to reproduce (sample project below): Tap "Start Fake Call". Within 5 seconds, tap the small route picker button and leave its list open. Wait — at t+5s the app reports the call connected via reportOutgoingCall(connectedAt:), and the list closes itself right after. Is this normal? And is there an official, CallKit-sanctioned way to report a call's connected state without this side effect — some way to keep the route picker stable across that transition? The two workarounds I've found both have real downsides: Calling reportOutgoingCall(connectedAt:) at the very start of the call, before the callee has actually answered , but then CallKit no longer reflects the real call state (Recents/duration would include ringing time, and calls that are never answered would still show as "connected"). Disabling audio device selection entirely until the call is established... works, but hurts the user experience, since users may want to switch output before the call connects. Here's the minimal reproduction: import CallKit import SwiftUI struct ContentView: View { @StateObject private var demo = CallKitDemo() var body: some View { VStack(spacing: 24) { Text(demo.statusText) .font(.system(.body, design: .monospaced)) .multilineTextAlignment(.center) Button("Start Fake Call") { demo.startFakeCall() } .disabled(demo.isCallInProgress) RoutePickerView() .frame(width: 60, height: 60) } .padding() } } private struct RoutePickerView: UIViewRepresentable { func makeUIView(context: Context) -> AVRoutePickerView { let picker = AVRoutePickerView() picker.delegate = context.coordinator return picker } func updateUIView(_ uiView: AVRoutePickerView, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator() } final class Coordinator: NSObject, AVRoutePickerViewDelegate { func routePickerViewWillBeginPresentingRoutes(_ routePickerView: AVRoutePickerView) { print("[REPRO]", Date(), "picker WILL present routes") } func routePickerViewDidEndPresentingRoutes(_ routePickerView: AVRoutePickerView) { print("[REPRO]", Date(), "picker DID end presenting routes <- dismissed here") } } } @MainActor final class CallKitDemo: NSObject, ObservableObject { @Published var statusText = "Idle" @Published var isCallInProgress = false private let provider: CXProvider = { let configuration = CXProviderConfiguration() configuration.supportsVideo = false configuration.maximumCallGroups = 1 configuration.maximumCallsPerCallGroup = 1 configuration.supportedHandleTypes = [.generic] return CXProvider(configuration: configuration) }() private let callController = CXCallController() private var currentCallUUID: UUID? override init() { super.init() provider.setDelegate(self, queue: nil) } func startFakeCall() { let uuid = UUID() currentCallUUID = uuid isCallInProgress = true statusText = "Requesting CXStartCallAction..." let handle = CXHandle(type: .generic, value: "repro-call") let startAction = CXStartCallAction(call: uuid, handle: handle) callController.request(CXTransaction(action: startAction)) { error in if let error { print("[REPRO]", Date(), "CXStartCallAction request failed:", error) } } DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] in guard let self, currentCallUUID == uuid else { return } print("[REPRO]", Date(), "reportOutgoingCall(connectedAt:)") statusText = "Reported connected at t+5s" provider.reportOutgoingCall(with: uuid, connectedAt: Date()) } DispatchQueue.main.asyncAfter(deadline: .now() + 8) { [weak self] in guard let self, currentCallUUID == uuid else { return } callController.request(CXTransaction(action: CXEndCallAction(call: uuid))) { _ in } currentCallUUID = nil isCallInProgress = false statusText = "Call ended" } } } extension CallKitDemo: CXProviderDelegate { func providerDidReset(_ provider: CXProvider) {} func provider(_ provider: CXProvider, perform action: CXStartCallAction) { action.fulfill() } func provider(_ provider: CXProvider, perform action: CXEndCallAction) { action.fulfill() } func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) { print("[REPRO]", Date(), "didActivate") } func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) { print("[REPRO]", Date(), "didDeactivate") } } Thanks for your help !
1
0
1.3k
Jul ’26
_MPRemoteCommandEventDispatch crashes on iOS 26.x devices.
I'm seeing crashes in _MPRemoteCommandEventDispatch on iOS 26.x devices in 3 apps. According to Bugsnag logs they are: NSInternalInconsistencyException: event dispatch <_MPRemoteCommandEventDispatch: <MPRemoteCommandEvent: 0x11c049500 commandID=THV0 command=<MPRemoteCommand: 0x109ad1ea0 type=Play (0) enabled=YES handlers=[0x109b6a310]> sourceID=(null) ([HostedRoutingSessionDataSource] handleControlSendingCommand<2W5E>)> state:201> deallocated without calling continuation I attached a log from Xcode organizer matching Bugsnag crash. mpr_remote_command_event.crash When I set the brakpoint on the -[_MPRemoteCommandEventDispatch dealloc] I can see it it's hit every time I tap play or pause on locked screen play button. Thread 0 Crashed: 0 libsystem_kernel.dylib 0x00000002370420cc __pthread_kill + 8 (:-1) 1 libsystem_pthread.dylib 0x00000001e975c810 pthread_kill + 268 (pthread.c:1721) 2 libsystem_c.dylib 0x0000000198f8ff64 abort + 124 (abort.c:122) 3 libc++abi.dylib 0x000000018a7cf808 __abort_message + 132 (abort_message.cpp:66) 4 libc++abi.dylib 0x000000018a7be484 demangling_terminate_handler() + 304 (cxa_default_handlers.cpp:76) 5 libobjc.A.dylib 0x000000018a6cff78 _objc_terminate() + 156 (objc-exception.mm:496) 6 xxxxxxxxxxxxxx 0x00000001003a7db8 CPPExceptionTerminate() + 416 (BSG_KSCrashSentry_CPPException.mm:156) 7 libc++abi.dylib 0x000000018a7cebdc std::__terminate(void (*)()) + 16 (cxa_handlers.cpp:59) 8 libc++abi.dylib 0x000000018a7ceb80 std::terminate() + 108 (cxa_handlers.cpp:88) 9 CoreFoundation 0x000000018d7341c4 __CFRunLoopPerCalloutARPEnd + 256 (CFRunLoop.c:769) 10 CoreFoundation 0x000000018d70bb5c __CFRunLoopRun + 1976 (CFRunLoop.c:3179) 11 CoreFoundation 0x000000018d70aa6c _CFRunLoopRunSpecificWithOptions + 532 (CFRunLoop.c:3462) 12 GraphicsServices 0x000000022e31c498 GSEventRunModal + 120 (GSEvent.c:2049) 13 UIKitCore 0x00000001930ceba4 -[UIApplication _run] + 792 (UIApplication.m:3902) 14 UIKitCore 0x0000000193077a78 UIApplicationMain + 336 (UIApplication.m:5577) 15 xxxxxxxxxxxxxx 0x00000001000c0134 main + 308 (main.swift:15) 16 dyld 0x000000018a722e28 start + 7116 (dyldMain.cpp:1477) Is the crash happening when the app is being terminated? Thank you!
8
3
2.9k
Jul ’26
Can an iOS app analyze audio being played by another app?
Hello, I'm in the early planning stages of an iOS app and I'm trying to determine what's technically possible before designing around assumptions. Is it possible for a third-party iOS app, with the user's permission, to access or analyze audio that is being played by another app (for example Apple Podcasts, Spotify, Audible, or YouTube) in real time? Or is microphone input the only supported way for an app to analyze audio that is audible in the user's environment? I'm not asking about recording the screen or capturing video. I'm specifically trying to understand whether another app's audio output is ever available to third-party apps, or whether iOS intentionally isolates apps from each other's audio. If the answer depends on the audio source (for example Apple Podcasts versus Spotify versus audio coming through the microphone), I'd appreciate understanding those distinctions as well. Thank you.
0
0
574
Jul ’26
CarPlay audio continues playing for approximately one second after the music is paused.
When my app is connected to CarPlay and music is playing, pausing playback from the device updates the playback state immediately. However, the audio continues to play for approximately one second before stopping. This issue only occurs with wireless CarPlay; playback pauses immediately with wired CarPlay. Is this expected behavior?
0
0
793
Jul ’26
Video recording goes fine but adding audio fails mysteriously
I'm trying to update an old unity app for a client. The app has been crashing on iOS in a plugin they use called NatCorder. They use it to record video only separately and then re-record it with effects and audio gathered separately. Instead of trying to update the plugin to something else which would be quite the hassle, I noticed the API for the native part of the plugin, where the crash occurs, is very simple, especially if you don't try to support everything the plugin does and the app does not use. So I tried to re-implement that native library using AVFoundation. I got the video recording right, it captures the camera from the iPhone and writes it to a file properly. However, when the app does the second part, where it sends video and audio frames to the plugin, it fails. The app sends all the video frames and then sends all the audio frames. The video frames are eaten fine by AVFoundation but the audio fails at random points with unknown errors. I wonder if I'm trying to use incompatible audio-video formats or if I'm using timestamps wrong or something. Here's my init code. Anything suspicious to you? void* NCCreateMP4Recorder(int width, int height, float framerate, int bitrate, int keyframeInterval, int sampleRate, int channelCount, const char* recordingPath, void (*callback)(void*, void*), void* context) { Recorder* recorder = calloc(1, sizeof(Recorder)); recorder->context = context; recorder->callback = callback; recorder->path = strdup(recordingPath); recorder->width = width; recorder->channelCount = channelCount; recorder->sampleRate = sampleRate; recorder->height = height; NSError *error = nil; NSURL* url = createURLFromArgumentCString(recordingPath); recorder->writer = [AVAssetWriter assetWriterWithURL:url fileType:AVFileTypeMPEG4 error:&error]; if (recorder->writer == nil) NSLog(@"Failed creating media writer: %@", error); NSDictionary *videoSettings = @{ AVVideoCodecKey: AVVideoCodecTypeH264, AVVideoWidthKey: @(width), AVVideoHeightKey: @(height), AVVideoCompressionPropertiesKey: @{ AVVideoAverageBitRateKey: @(bitrate), AVVideoMaxKeyFrameIntervalKey: @(keyframeInterval), } }; recorder->video = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings]; if (recorder->video == nil) NSLog(@"Failed creating video writer input"); recorder->video.expectsMediaDataInRealTime = true; NSDictionary* videoSource = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey, [NSNumber numberWithInt:width], kCVPixelBufferWidthKey, [NSNumber numberWithInt:height], kCVPixelBufferHeightKey, nil]; recorder->videoAdaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:recorder->video sourcePixelBufferAttributes:videoSource]; if (recorder->videoAdaptor == nil) NSLog(@"Failed creating video adaptor"); if ([recorder->writer canAddInput:recorder->video]) [recorder->writer addInput:recorder->video]; else NSLog(@"Could not add video input to writer"); if (sampleRate > 0 && channelCount > 0) { AudioChannelLayout layout = { .mChannelLayoutTag = channelCount == 1 ? kAudioChannelLayoutTag_Mono : kAudioChannelLayoutTag_Stereo, .mChannelBitmap = 0, .mNumberChannelDescriptions = 0 }; NSDictionary* audioOutputSettings = @{ AVFormatIDKey: @(kAudioFormatMPEG4AAC), AVNumberOfChannelsKey: @(channelCount), AVSampleRateKey: @(sampleRate), AVEncoderBitRateKey: @128000, AVChannelLayoutKey: [NSData dataWithBytes:&layout length:sizeof(layout)] }; recorder->audio = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:audioOutputSettings]; if (!recorder->audio) NSLog(@"Failed creating audio adaptor"); recorder->audio.expectsMediaDataInRealTime = true; AudioStreamBasicDescription audioStreamDesc = { .mSampleRate = sampleRate, .mFormatID = kAudioFormatLinearPCM, .mFormatFlags = kAudioFormatFlagIsPacked | kAudioFormatFlagIsFloat, .mBytesPerPacket = channelCount * sizeof(float), .mFramesPerPacket = 1, .mBytesPerFrame = channelCount * sizeof(float), .mChannelsPerFrame = channelCount, .mBitsPerChannel = sizeof(float) * 8, }; OSStatus status = CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &audioStreamDesc, sizeof(layout), &layout, 0, nil, nil, &recorder->audioDesc); if (status) NSLog(@"Failed creating audio format description: %d", (int)status); if ([recorder->writer canAddInput:recorder->audio]) [recorder->writer addInput:recorder->audio]; else NSLog(@"Could not add audio input to writer"); } if (![recorder->writer startWriting]) NSLog(@"Could not start writing: %@", recorder->writer.error); [recorder->writer startSessionAtSourceTime:kCMTimeZero]; NSLog(@"Recording started to %s", recordingPath); return recorder; }
Replies
2
Boosts
0
Views
346
Activity
Aug ’26
Processing / tapping an HLS audio stream (or global app output)
I'm trying to do some realtime audio processing on audio served from an HLS stream (i.e. an AVPlayer created using an M3U HTTP URL). It doesn't seem like attaching an AVAudioMix configured with with an `audioTapProcessor` has any effect; none of the callbacks except `init` are being invoked. Is this a known limitation? If so, is this documented somewhere?If the above is a limitation, what are my options using some of the other audio APIs? I looked into `AVAudioEngine` as well but it doesn't seem like there's any way I can configure any of the input node types to use an HLS stream. Am I wrong? Are there lower level APIs available to play HLS streams that provide the necessary hooks?Alternatively, is there some generic way to tap into all audio being output by my app regardless of its source?Thanks a lot!
Replies
12
Boosts
0
Views
5.5k
Activity
Aug ’26
Preserve AirPods Transparency while capturing one AirPod microphone for low latency cross ear audio
I am prototyping an iOS accessibility audio application for people with unilateral hearing loss. The goal is simple. Capture environmental audio using the AirPod on the poor hearing side and route that audio with very low latency into the AirPod on the working hearing side. The core concept is already working on physical AirPods and an iPhone. Our desired signal path is Right AirPod microphone → iPhone audio processing → Left AirPod output The user sets the AirPods microphone in Settings to Always Right AirPod. The app then captures that Bluetooth microphone and routes the signal into the left output channel. Using AVAudioSession and AVAudioEngine we have successfully achieved low latency cross ear audio. The main issue is native AirPods Transparency. When the app activates the AirPods Bluetooth microphone using HFP, the transferred audio continues working correctly, but native Transparency audio on the receiving AirPod effectively disappears. Interestingly, iOS still reports Transparency as enabled in Control Center and AirPods settings. This means the user can hear audio transferred from the poor hearing side, but their working ear loses much of the normal environmental sound that Transparency previously provided. We have tested several configurations. Normal Bluetooth HFP with playAndRecord This provides the best result so far. The AirPods microphone signal is strong. The output route exposes two channels. We can route the microphone signal only to the opposite AirPod. Latency can be reduced enough that conversation feels nearly real time. However, native Transparency on the receiving AirPod effectively stops providing environmental audio. bluetoothHighQualityRecording Microphone quality is noticeably better. However, latency is significantly higher and produces an audible delayed or echo effect during conversation. Transparency has the same general issue. multiRoute with dualRoute This allows the iPhone audio hardware and AirPods to operate simultaneously. It appears to preserve more ambient awareness on the working side. However, the AirPods microphone becomes substantially weaker when used as the secondary Bluetooth HFP input. We measured the incoming AirPods microphone signal at roughly minus 40 dB during normal speech testing. Adding 12 to 18 dB of software gain increases distortion without materially improving intelligibility. farFieldInput with dualRoute We tested the raw AirPods microphone with and without farFieldInput. The microphone levels and practical pickup were very similar. Far field speech processing improved intelligibility somewhat, but did not solve the weak secondary Bluetooth input signal. Our main technical question is Is there a supported AVAudioSession configuration, Core Audio API, AirPods API, or entitlement that allows a third party application to capture an AirPods microphone while preserving the AirPods native Transparency processing on the output AirPod? Ideally we want Right AirPod microphone capture plus native Transparency on the left AirPod plus application generated right to left audio mixed into the left AirPod all operating simultaneously. We do not need access to Apple's Transparency microphone signal itself if native Transparency can simply remain active while our application audio is mixed into the receiving AirPod. If this is not currently possible using public APIs, is this an intentional platform limitation of the AirPods Bluetooth microphone route? We would also appreciate guidance on whether there is another recommended architecture for this accessibility use case. The low latency cross ear routing itself works surprisingly well. Preserving natural environmental audio in the user's working ear while the Bluetooth microphone is active is currently the main technical blocker. Testing has been performed on a physical iPhone running iOS 26.x using Xcode 26.6 and current AirPods hardware, not the Simulator. Thank you for any guidance.
Replies
0
Boosts
0
Views
316
Activity
Aug ’26
watchOS: Network framework WebSocket loses its path ~35 s in, while URLSession keeps working
Following up on TN3135 and the resolution in https://developer.apple.com/forums/thread/773362 — that thread solved establishing a low-level connection on watchOS (the asynchronous AVAudioSession.activate(options:completionHandler:) instead of the synchronous setActive()). This question is about a connection staying established, which I could not find discussed anywhere. Environment: Apple Watch, watchOS 26.6 (23U67). Audio app, WKBackgroundModes = ["self-care"]. Real device, TestFlight build, not the simulator. What works Opening an NWConnection WebSocket to my own server is reliable — 8 attempts out of 8 reached .ready in 0.28–0.98 s, and an echo frame round-tripped in 27–89 ms. Interestingly, in my measurements it opens under BOTH activation variants: the asynchronous activate(options:completionHandler:) AND the synchronous setActive(true). The two are within ~0.2 s of each other. I mention it only because the thread above concluded the synchronous one is insufficient; on 26.6 I cannot reproduce that difference for establishment. What fails The connection goes quiet after roughly half a minute, and an NWPathMonitor running alongside it shows why: the path transitions to .unsatisfied. Four runs: +34.0 s (cellular) +34.6 s (cellular) +36.0 s (Wi-Fi) +34.3 s (companion link only — availableInterfaces ["other", "other"]) The server sends a heartbeat frame every 5 s and closes the socket on a schedule, so I can tell "the peer closed" from "we stopped receiving". The client receives beats 1–6 (5 s … 30 s) and then nothing; the scheduled close never arrives. What I ruled out Server side. The same client construction run on macOS against the same endpoint receives all 8 heartbeats and the scheduled close at 45.1 s. Both audio-session activation variants — no difference, as above. Network type — cellular, Wi-Fi and companion-link-only all drop at ~35 s. The app being suspended. The app keeps logging densely throughout, and in the last run it held a WKExtendedRuntimeSession (delegate reported extendedRuntimeSessionDidStart) and was actively playing audio through AVAudioEngine from the first second — i.e. the audio-streaming condition TN3135 describes — for the entire window. The path dropped anyway, at +34.3 s. An idle socket. Server traffic arrives every 5 s until the drop. The comparison that puzzles me The same app, on the same watch, the same afternoon, relays the same realtime audio session over plain HTTPS (URLSession) instead — and that runs for 64 s continuously without a stall, including straight through a WatchConnectivity "reachability settled: unreachable" transition. So a high-level URLSession request stream survives a period in which a low-level NWConnection's path is reported unsatisfied. That is consistent with the note in thread 773362 that "on watchOS every session is kinda like a background session, where the actual work is done out of process" — but it leaves me unsure what the intended behaviour is. Questions Is a ~35 s path lifetime the expected behaviour for low-level networking on watchOS, or does it indicate something wrong on my side? Does the TN3135 audio-streaming exception cover only the establishment of a low-level connection, or is it also supposed to keep the path available for the duration of the audio streaming? If it is supposed to persist: is there something beyond an active audio session, flowing audio and a WKExtendedRuntimeSession that an app must do to keep the path alive? If ~35 s is the expected ceiling, is a WebSocket a supported transport for a multi-minute conversational audio session on watchOS at all — or is relaying over URLSession the intended approach despite the guidance to prefer Network framework? Happy to file a bug with a sysdiagnose and a reduced sample project if that is more useful — please say the word and I will attach the numbers above.
Replies
4
Boosts
0
Views
1.8k
Activity
Aug ’26
Setting appEntityIdentifiers on Now Playing content from a RemoteMediaSessionExtension
I'm using the new RemoteMediaSession API (iOS 27) to surface a remote device's playback (network speakers) on the Lock Screen / Control Center. I'd like to link the presented MusicContent to my App Intents entities so Siri can answer "what's playing?" / "tell me more about this artist," using appEntityIdentifiers. The problem: that property is unavailable in extensions. @available(iOSApplicationExtension, unavailable) extension MediaContentRepresentable { public var appEntityIdentifiers: [EntityIdentifier] { get set } } Result: an extension-hosted remote session seems to have no supported way to attach App Intents entity identifiers to its content. A local MediaSession can set it, but only while the app is running. Questions: Is there a supported way to associate appEntityIdentifiers with RemoteMediaSession content that I'm missing? If not, is this an intentional limitation? I've filed an enhancement request — FB24301827. Any guidance appreciated. Thanks!
Replies
0
Boosts
0
Views
560
Activity
Aug ’26
MacOS Music App Returning 404 to Play Next Commands from iTunes Remote app
MacOS Music App No Longer Accepts "Play Next" and "Add to Up Next" from iTunes Remote app. Connect to your library through the iTunes Remote App. Navigate to a song within the iOS iTunes Remote App and press and hold on the song and when the action sheet comes up select "Play Next" or "Add to Up Next". View the MacOS Music App's Playing Next queue to discover that the additions were not made. Thanks for any help you can provide on this. I'd really love to see this working again. I captured network traffic (tcpdump) between the iTunes Remote iOS app and Music.app on macOS 26 (Tahoe). When tapping "Play Next" on a track, the Remote app sends: GET /ctrl-int/1/playqueue-edit?command=add&query='dmap.itemid:27387'&sort=album&mode=3 Music.app responds with HTTP 404 Not Found. "Add to Up Next" sends the same endpoint with mode=0 and also receives 404 Not Found. Other Remote app functions work correctly over the same connection (play/pause, skip), browsing the library, and viewing the queue all return successful responses. Only the queue-add operation returns 404.
Replies
0
Boosts
0
Views
698
Activity
Aug ’26
.longFormAudio and USB mic input
I am trying to stream audio from a USB input to a set of AirPlay speakers. I can get this to work to a single AirPlay speaker when I use .playAndRecord and don't use .longFormAudio in the AVAudioSession setup but I hear some audio glitches. I believe these glitches to be audio under-run at the speaker due to differences in clock rates, etc. As I understand the API, to get rid of the audio glitches, I need to use .longFormAudio to enable AirPlay2 and get the speaker to deal with tracking the audio sample timing and have the speaker do any re-sampling when the clocks drift. But if I turn on .longFormAudio, the API will not allow me to use .playAndRecord. Is there a way to get AirPlay2 re-timing behaviors and also enable mic input in the same IOS app?
Replies
0
Boosts
0
Views
670
Activity
Aug ’26
Apple-supported alternative to MusicKit JS authorization for child accounts
I’m developing a dedicated children’s audio player using MusicKit JS. Ideally, a child would have access to their own Apple Music library and listening history while remaining managed through Family Sharing. Apple Developer Support confirmed that MusicKit cannot be authorized for an under-13 Apple Account due to age restrictions. Is there an Apple-supported alternative, such as parent authorization with access to a child’s library through any other SDK/API path?
Replies
0
Boosts
0
Views
700
Activity
Aug ’26
Why isn’t Audio Output a per-app permission, like Microphone?
iOS already gives users a simple per-app Microphone permission: Settings → Apps → [App] → Microphone: On/Off Why isn’t there an equivalent permission in the other direction? Settings → Apps → [App] → Audio Output: On/Off This would solve a surprisingly common problem: I may deliberately be listening to Spotify, an audiobook or a podcast, then open another app which suddenly produces audio from an advertisement or autoplaying video. That audio may mix with, duck, or even interrupt what I actually chose to listen to. As I understand the current architecture, apps use AVAudioSession to describe how their audio should interact with other audio. But much of that policy is therefore controlled by the application producing the unwanted audio, rather than by the device owner. The simplest solution wouldn’t require a per-app mixer or complicated audio controls. Just one user-controlled permission: Allow Audio Output: On / Off When disabled, iOS would prevent that app from producing audible media output, while audio sessions belonging to other apps would continue normally. Conceptually, this seems very similar to the existing Microphone permission: Microphone: Can this app receive audio from my device? Audio Output: Can this app produce audio on my device? More advanced controls — per-app volume, permission to interrupt other audio, ducking policy, etc. — could potentially come later. But they aren’t necessary to solve the fundamental problem. I’m curious from an AVAudioSession perspective: Is there a technical reason why iOS could not enforce an OS-level per-app Audio Output permission in the same way it already enforces Microphone access? And if there isn’t, would others find this useful?
Replies
0
Boosts
0
Views
560
Activity
Aug ’26
Fetch tracks from a playlist
If an app allows people to create a playlist and add more songs to that created playlist, it would make sense to guard them from accidentally adding the same song to the playlist more than once. In this code, even though it is successfully receiving the existing playlist from the request, its tracks and entries always show as nil even when there are songs in the playlist. Any suggestions for how to guard against adding duplicates to a playlist? Thank you! var request = MusicLibraryRequest<Playlist>() request.filter(matching: \.name, equalTo: "AppGeneratedPlaylist") let response = try await request.response() if let existingPlaylist = response.items.first { if let tracks = existingPlaylist.entries, tracks.contains(where: { $0.id == song.id }) { print("Song is already in the playlist, so don't add again") return } else { try await MusicLibrary.shared.add(song, to: existingPlaylist) print("Added song to existing playlist: \(existingPlaylist.name)") print("Count of tracks: \(existingPlaylist.tracks?.count)") print("Count of entries: \(existingPlaylist.entries?.count)") print("Current tracks: \(existingPlaylist.tracks?.map(\.id))") print("Current entries: \(existingPlaylist.entries?.map(\.id))") } }
Replies
1
Boosts
0
Views
1.1k
Activity
Aug ’26
Native WebRTC remote audio stops after ~1 hour while Safari still plays the same stream
Hello, I am developing BROXMEDIA Intercom, an iOS intercom application for live audiovisual production. The app uses a native Swift audio plugin, Google WebRTC, AVAudioSession, and a Capacitor user interface. The current TestFlight version is 2.0, build 17. Environment: iPhone 16 Pro Max iOS 26.5.2 TestFlight internal build AVAudioSession category: playAndRecord AVAudioSession mode: voiceChat Background audio capability enabled Bidirectional WebRTC audio between a web browser and the native iOS app Observed behavior: A remote web browser publishes WebRTC audio. The native iOS app receives and plays the audio correctly for approximately one hour. Wi-Fi disconnection/reconnection and airplane mode on/off initially recover correctly. After the prolonged session, the native app stops playing the remote audio. Signaling and participant presence remain connected. The remote participant is still shown as speaking. Completely closing and reopening the native app does not recover the remote audio. Safari on the same iPhone, connected to the same room and network, can still hear the same remote transmission. Restarting the remote web publication usually causes the native app to receive audio again. This suggests that the remote publication, network connection, signaling server, and device audio hardware are still operational when the native route fails. We are investigating whether: AVAudioSession or the underlying WebRTC audio unit has stopped rendering; the native RTCPeerConnection retains a stale receiver or audio track; inbound RTP has stopped even though the peer remains connected; an interruption, route change, or media-services reset has not been fully recovered. Our current recovery logic checks the peer connection state and whether a remote audio track object exists. However, we do not yet continuously verify that inbound RTP packets or bytes are increasing for each participant. Questions: Can AVAudioSession or its underlying audio unit stop rendering audio while RTCPeerConnection signaling remains connected? Which AVAudioSession or audio-unit callbacks should be monitored to distinguish an iOS audio-session failure from a WebRTC receiver or inbound-RTP failure? After AVAudioSession.mediaServicesWereResetNotification, should an app recreate the complete WebRTC audio engine, or is reactivating AVAudioSession normally sufficient? Is monitoring inbound RTP progression and audio energy the recommended way to detect a remote audio track that still exists but is no longer delivering usable audio? Are there any known considerations for prolonged bidirectional VoIP-style audio using playAndRecord, voiceChat, and background audio? We can add diagnostic logging and provide a Feedback Assistant report with sysdiagnose if the problem is reproduced again. Thank you.
Replies
0
Boosts
0
Views
441
Activity
Aug ’26
VPIO Audio Ducking
Hey y'all, I'm new here and in the process of building some audio software. I'm hitting a roadblock trying to incorporate VPIO into audio playback states without it ducking what's currently playing in my DAW. I have a few questions: Can VPIO other-audio ducking be completely disabled on macOS, beyond fixed/minimum? Minimum is not enough for professional audio environments. Is attenuation of an unrelated application routed through a different Core Audio output device expected? Is split-device VPIO—built-in microphone input with Apollo/Universal Audio output—supported? Why can AVAudioEngine.start() succeed while the engine remains stopped and immediately emits a configuration-change notification? Thanks for any insight into this!
Replies
0
Boosts
0
Views
1.2k
Activity
Jul ’26
AVRoutePickerView's presented route list is automatically dismissed when CXProvider reports the call as connected (`reportOutgoingCall(with:connectedAt:)`)
Hi, I want my user to be able to change their audio output device while calling someone, even before the call is established. But I've run into an issue combining CallKit and AVRoutePickerView. On iOS 26 (Xcode 26.5, tested on iPhone 13 Mini): when my AVRoutePickerView's route list is presented while my call is still connecting (not yet established), the presented list is automatically dismissed the moment the app reports the call as connected via CXProvider.reportOutgoingCall(with:connectedAt:). That doesn't seem natural to me — it should stay open, since the user is actively interacting with unrelated system UI at that moment. This happens even when nothing about the AVAudioSession itself changes: no category change, no route change, no didActivate/didDeactivate callback fires around that time. The dismissal is caused by the CallKit "connected" state transition itself, but I don't know why, or whether it's intentional. Steps to reproduce (sample project below): Tap "Start Fake Call". Within 5 seconds, tap the small route picker button and leave its list open. Wait — at t+5s the app reports the call connected via reportOutgoingCall(connectedAt:), and the list closes itself right after. Is this normal? And is there an official, CallKit-sanctioned way to report a call's connected state without this side effect — some way to keep the route picker stable across that transition? The two workarounds I've found both have real downsides: Calling reportOutgoingCall(connectedAt:) at the very start of the call, before the callee has actually answered , but then CallKit no longer reflects the real call state (Recents/duration would include ringing time, and calls that are never answered would still show as "connected"). Disabling audio device selection entirely until the call is established... works, but hurts the user experience, since users may want to switch output before the call connects. Here's the minimal reproduction: import CallKit import SwiftUI struct ContentView: View { @StateObject private var demo = CallKitDemo() var body: some View { VStack(spacing: 24) { Text(demo.statusText) .font(.system(.body, design: .monospaced)) .multilineTextAlignment(.center) Button("Start Fake Call") { demo.startFakeCall() } .disabled(demo.isCallInProgress) RoutePickerView() .frame(width: 60, height: 60) } .padding() } } private struct RoutePickerView: UIViewRepresentable { func makeUIView(context: Context) -> AVRoutePickerView { let picker = AVRoutePickerView() picker.delegate = context.coordinator return picker } func updateUIView(_ uiView: AVRoutePickerView, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator() } final class Coordinator: NSObject, AVRoutePickerViewDelegate { func routePickerViewWillBeginPresentingRoutes(_ routePickerView: AVRoutePickerView) { print("[REPRO]", Date(), "picker WILL present routes") } func routePickerViewDidEndPresentingRoutes(_ routePickerView: AVRoutePickerView) { print("[REPRO]", Date(), "picker DID end presenting routes <- dismissed here") } } } @MainActor final class CallKitDemo: NSObject, ObservableObject { @Published var statusText = "Idle" @Published var isCallInProgress = false private let provider: CXProvider = { let configuration = CXProviderConfiguration() configuration.supportsVideo = false configuration.maximumCallGroups = 1 configuration.maximumCallsPerCallGroup = 1 configuration.supportedHandleTypes = [.generic] return CXProvider(configuration: configuration) }() private let callController = CXCallController() private var currentCallUUID: UUID? override init() { super.init() provider.setDelegate(self, queue: nil) } func startFakeCall() { let uuid = UUID() currentCallUUID = uuid isCallInProgress = true statusText = "Requesting CXStartCallAction..." let handle = CXHandle(type: .generic, value: "repro-call") let startAction = CXStartCallAction(call: uuid, handle: handle) callController.request(CXTransaction(action: startAction)) { error in if let error { print("[REPRO]", Date(), "CXStartCallAction request failed:", error) } } DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] in guard let self, currentCallUUID == uuid else { return } print("[REPRO]", Date(), "reportOutgoingCall(connectedAt:)") statusText = "Reported connected at t+5s" provider.reportOutgoingCall(with: uuid, connectedAt: Date()) } DispatchQueue.main.asyncAfter(deadline: .now() + 8) { [weak self] in guard let self, currentCallUUID == uuid else { return } callController.request(CXTransaction(action: CXEndCallAction(call: uuid))) { _ in } currentCallUUID = nil isCallInProgress = false statusText = "Call ended" } } } extension CallKitDemo: CXProviderDelegate { func providerDidReset(_ provider: CXProvider) {} func provider(_ provider: CXProvider, perform action: CXStartCallAction) { action.fulfill() } func provider(_ provider: CXProvider, perform action: CXEndCallAction) { action.fulfill() } func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) { print("[REPRO]", Date(), "didActivate") } func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) { print("[REPRO]", Date(), "didDeactivate") } } Thanks for your help !
Replies
1
Boosts
0
Views
1.3k
Activity
Jul ’26
_MPRemoteCommandEventDispatch crashes on iOS 26.x devices.
I'm seeing crashes in _MPRemoteCommandEventDispatch on iOS 26.x devices in 3 apps. According to Bugsnag logs they are: NSInternalInconsistencyException: event dispatch <_MPRemoteCommandEventDispatch: <MPRemoteCommandEvent: 0x11c049500 commandID=THV0 command=<MPRemoteCommand: 0x109ad1ea0 type=Play (0) enabled=YES handlers=[0x109b6a310]> sourceID=(null) ([HostedRoutingSessionDataSource] handleControlSendingCommand<2W5E>)> state:201> deallocated without calling continuation I attached a log from Xcode organizer matching Bugsnag crash. mpr_remote_command_event.crash When I set the brakpoint on the -[_MPRemoteCommandEventDispatch dealloc] I can see it it's hit every time I tap play or pause on locked screen play button. Thread 0 Crashed: 0 libsystem_kernel.dylib 0x00000002370420cc __pthread_kill + 8 (:-1) 1 libsystem_pthread.dylib 0x00000001e975c810 pthread_kill + 268 (pthread.c:1721) 2 libsystem_c.dylib 0x0000000198f8ff64 abort + 124 (abort.c:122) 3 libc++abi.dylib 0x000000018a7cf808 __abort_message + 132 (abort_message.cpp:66) 4 libc++abi.dylib 0x000000018a7be484 demangling_terminate_handler() + 304 (cxa_default_handlers.cpp:76) 5 libobjc.A.dylib 0x000000018a6cff78 _objc_terminate() + 156 (objc-exception.mm:496) 6 xxxxxxxxxxxxxx 0x00000001003a7db8 CPPExceptionTerminate() + 416 (BSG_KSCrashSentry_CPPException.mm:156) 7 libc++abi.dylib 0x000000018a7cebdc std::__terminate(void (*)()) + 16 (cxa_handlers.cpp:59) 8 libc++abi.dylib 0x000000018a7ceb80 std::terminate() + 108 (cxa_handlers.cpp:88) 9 CoreFoundation 0x000000018d7341c4 __CFRunLoopPerCalloutARPEnd + 256 (CFRunLoop.c:769) 10 CoreFoundation 0x000000018d70bb5c __CFRunLoopRun + 1976 (CFRunLoop.c:3179) 11 CoreFoundation 0x000000018d70aa6c _CFRunLoopRunSpecificWithOptions + 532 (CFRunLoop.c:3462) 12 GraphicsServices 0x000000022e31c498 GSEventRunModal + 120 (GSEvent.c:2049) 13 UIKitCore 0x00000001930ceba4 -[UIApplication _run] + 792 (UIApplication.m:3902) 14 UIKitCore 0x0000000193077a78 UIApplicationMain + 336 (UIApplication.m:5577) 15 xxxxxxxxxxxxxx 0x00000001000c0134 main + 308 (main.swift:15) 16 dyld 0x000000018a722e28 start + 7116 (dyldMain.cpp:1477) Is the crash happening when the app is being terminated? Thank you!
Replies
8
Boosts
3
Views
2.9k
Activity
Jul ’26
Can an iOS app analyze audio being played by another app?
Hello, I'm in the early planning stages of an iOS app and I'm trying to determine what's technically possible before designing around assumptions. Is it possible for a third-party iOS app, with the user's permission, to access or analyze audio that is being played by another app (for example Apple Podcasts, Spotify, Audible, or YouTube) in real time? Or is microphone input the only supported way for an app to analyze audio that is audible in the user's environment? I'm not asking about recording the screen or capturing video. I'm specifically trying to understand whether another app's audio output is ever available to third-party apps, or whether iOS intentionally isolates apps from each other's audio. If the answer depends on the audio source (for example Apple Podcasts versus Spotify versus audio coming through the microphone), I'd appreciate understanding those distinctions as well. Thank you.
Replies
0
Boosts
0
Views
574
Activity
Jul ’26
CarPlay audio continues playing for approximately one second after the music is paused.
When my app is connected to CarPlay and music is playing, pausing playback from the device updates the playback state immediately. However, the audio continues to play for approximately one second before stopping. This issue only occurs with wireless CarPlay; playback pauses immediately with wired CarPlay. Is this expected behavior?
Replies
0
Boosts
0
Views
793
Activity
Jul ’26