AVFoundation

RSS for tag

Work with audiovisual assets, control device cameras, process audio, and configure system audio interactions using AVFoundation.

Posts under AVFoundation tag

200 Posts

Post

Replies

Boosts

Views

Activity

Does the TN3135 audio-session networking exception have a defined lifetime? Seeing a ~38.5 s revoke/re-grant cycle
TN3135 describes the exception that lets a watchOS app use low-level networking while it holds an active audio session. I have that working, and the app functions — but the network path is withdrawn and restored on a strikingly regular cycle, and I would like to know whether that is expected behaviour rather than something I am doing wrong. Setup Apple Watch Series 10 (Watch7,9), watchOS 26.5. Reproduced on a Series 6 (Watch6,2). UIBackgroundModes: [audio]; AVAudioSession category .playAndRecord, mode .spokenAudio; activated with the async activate(options:completionHandler:). NWConnection with NWProtocolWebSocket to a WebSocket relay over TLS. The app streams 16 kHz mono PCM continuously while transmitting and holds the socket open otherwise. Symptom NWPathMonitor reports .unsatisfied, then .satisfied about two seconds later, over and over. Measured with the iPhone powered off, so the watch was on its own Wi-Fi: Uptime between drops Outage 36.4 s 2.1 s 36.7 s 1.9 s 36.9 s 2.1 s The regularity is what prompts the question — uptime varies by ±0.3 s and the outage is consistently 2.0 s. That reads as a timeout expiring rather than radio behaviour. What I have ruled out Not the network or the server. A browser client on the same relay, same TLS, same wire protocol, holds a WebSocket indefinitely. Not the interface. Identical cadence over the companion ipsec1 tunnel with the iPhone present, and over the watch's own en0 with the iPhone powered off. Pinning requiredInterfaceType = .wifi while the iPhone is reachable fails outright — the path offers only ipsec1. Not audio-session interruption. I observe interruptionNotification, routeChangeNotification, mediaServicesWereResetNotification and silenceSecondaryAudioHintNotification. None fire at a drop. At the moment the path goes .unsatisfied, the engine is running and the player node is actively playing. Not session idleness. Playing continuous silence for the whole session, rather than only while reconnecting, made no difference — still 36.4 s. The control that surprised me To test whether this affects any long-lived watch socket or only audio-unlocked ones, I built a second app with no AVAudioSession at all, no audio background mode, holding a URLSessionWebSocketTask and kept alive by a WKExtendedRuntimeSession so screen sleep was not a factor. It never connected. NWPathMonitor reported .unsatisfied once and never changed, across a 30 s run, and every request failed with "The Internet connection appears to be offline." I had expected URLSession to be permitted regardless. Questions Does the audio-session networking exception in TN3135 have a defined lifetime, and is a periodic revoke/re-grant cycle expected? If so, is there a supported way to hold it continuously — or is the correct design simply to expect the interruption and reconnect through it? Is it expected that an app with no audio session gets no network path at all on watchOS, including via URLSession, even in the foreground with an extended runtime session?
1
0
48
18h
AVPlayerItemSampleBufferOutput
I am using AVPlayer, but I am unable to retrieve PCM data from .m3u8 streams via the AVPlayerItemSampleBufferOutput API in iOS 27 beta. I have tried both the Objective-C and Swift APIs: the Objective-C delegate methods are not being called, and the Swift methods nextAvailableSampleBuffer() and nextSampleBuffer() are returning no data.
0
0
24
22h
fail to get HLS realtime stream via AVPlayerItemSampleBufferOutputDelegate
Hi, 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). And I find that new APIs(AVPlayerItemSampleBufferOutput) are available in iOS 27.0 to achieve it. However, I failed to get the available data via the AVPlayerItemSampleBufferOutputDelegate. And I found some error in the system log: I create AVPlayerItemSampleBufferOutput and set the delegate after receiving the AVPlayerItemStatusReadyToPlay event. And here's my code: @interface OCAudioSamplebuffer () <AVPlayerItemSampleBufferOutputDelegate> @property (nonatomic, strong) AVPlayerItemSampleBufferOutput *bufferOutput; @property (nonatomic, strong) dispatch_queue_t bufferOutputQueue; @property (nonatomic, strong) AVPlayerItem *playerItem; @end - (void)playItem:(AVPlayerItem *)item { if (@available(iOS 27, *)) { AVAudioSession *audioSession = [AVAudioSession sharedInstance]; if([NSThread mainThread]){ [audioSession setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryPlayback error:nil]; [audioSession setActive:YES error:nil]; }else{ dispatch_async(dispatch_get_main_queue(), ^{ [audioSession setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryPlayback error:nil]; [audioSession setActive:YES error:nil]; }); } AVPlayerItemSampleBufferOutputAudioConfiguration *cfg = [[AVPlayerItemSampleBufferOutputAudioConfiguration alloc] init]; CMFormatDescriptionRef formatDescription = [self createPCMFormatDescriptionWithSampleRate:44100.0 channels:2 isFloat:YES]; cfg.requestedAudioFormat = formatDescription; if (formatDescription) { NSLog(@"create PCM Format Description success"); CFRelease(formatDescription); } else { NSLog(@"fail to create PCM Format Description"); } self.bufferOutput = [[AVPlayerItemSampleBufferOutput alloc] initWithConfiguration:cfg]; NSLog(@"create buffer success, PCM Format Description%@---,%@", cfg.requestedAudioFormat, formatDescription); self.bufferOutputQueue = dispatch_queue_create("audioSamplebufferQueue", DISPATCH_QUEUE_CONCURRENT); [self.bufferOutput setDelegate:self queue:self.bufferOutputQueue]; [item addOutput:self.bufferOutput]; self.playerItem = item; } else { // Fallback on earlier versions } } Any help. Thank you
1
0
346
1d
flashMode .on overrides locked focus — AF scan runs and refocuses before the strobe
I'm building a fixed-focus camera app (Bayer RAW, manual exposure, focus permanently locked at a known lens position) and need a full-power flash still that keeps that locked focus. On iPhone 17 Pro, iOS 26, any capture with flashMode = .on runs an autofocus-assist scan — visible lens hunt, assist lamp in low light — and the exposure happens at whatever the scan converged on (usually the far background), not my locked position. In low light it reproduces every time; bright-scene behavior varied by configuration (see matrix), and in my current build it hunts in daylight too. This follows https://developer.apple.com/forums/thread/724897 where DTS confirmed locked lens position + flash "is possible" in a test app, but the differentiating configuration was never identified. I've now exhausted every documented lever, with instrumentation, and would like guidance on whether this stage is bypassable at all. Session configuration session.sessionPreset = .photo // Plain wide camera (also reproduced on .builtInLiDARDepthCamera). let camera = AVCaptureDevice.default( .builtInWideAngleCamera, for: .video, position: .back )! session.addInput(try AVCaptureDeviceInput(device: camera)) session.addOutput(photoOutput) photoOutput.maxPhotoQualityPrioritization = .speed photoOutput.isZeroShutterLagEnabled = false photoOutput.isResponsiveCaptureEnabled = true // Pre-allocate flash capture resources up front. photoOutput.setPreparedPhotoSettingsArray( [makeFlashRawSettings()], completionHandler: nil ) Device configuration (before any capture) try camera.lockForConfiguration() camera.isSubjectAreaChangeMonitoringEnabled = false camera.isSmoothAutoFocusEnabled = false camera.autoFocusRangeRestriction = .near camera.automaticallyEnablesLowLightBoostWhenAvailable = false camera.automaticallyAdjustsFaceDrivenAutoFocusEnabled = false camera.isFaceDrivenAutoFocusEnabled = false // true is worse; see matrix camera.automaticallyAdjustsFaceDrivenAutoExposureEnabled = false camera.isFaceDrivenAutoExposureEnabled = false camera.setFocusModeLocked(lensPosition: 0.60) // ~1 m on this device let gains = camera.deviceWhiteBalanceGains( for: .init(temperature: 4000, tint: 0) ) camera.setWhiteBalanceModeLocked(with: gains) camera.setExposureModeCustom( duration: CMTime(value: 1, timescale: 125), iso: 400 ) camera.unlockForConfiguration() Capture func makeFlashRawSettings() -> AVCapturePhotoSettings { let bayer = photoOutput.availableRawPhotoPixelFormatTypes.first { AVCapturePhotoOutput.isBayerRAWPixelFormat($0) }! let settings = AVCapturePhotoSettings(rawPixelFormatType: bayer) settings.flashMode = .on settings.isAutoRedEyeReductionEnabled = false settings.isAutoStillImageStabilizationEnabled = false settings.isAutoVirtualDeviceFusionEnabled = false settings.isAutoContentAwareDistortionCorrectionEnabled = false settings.photoQualityPrioritization = .speed return settings } // Right before the request, exposure flips .custom → .locked (same // duration/ISO), one preview frame presents, then: photoOutput.capturePhoto(with: makeFlashRawSettings(), delegate: self) What I measure (KVO during the capture window) isAdjustingFocus goes true after the request: a scan runs despite focusMode == .locked (assist lamp on in low light). lensPosition moves from the locked 0.60 to a far position and stays there through willCapturePhotoFor — the exposure uses the scan's answer. When isAdjustingFocus falls back to false (before the metering preflash) I issue one setFocusModeLocked(lensPosition: 0.60). The call succeeds, yet the exposure still happens at the far position — something re-asserts the scan's focus before the strobe. With flashMode = .off, locked focus and custom exposure are honored perfectly (my app's normal path). Tried, all reproduced focusMode = .locked + setFocusModeLocked(lensPosition:): scan still runs. Exposure .custom → .locked for the flash window: no change. Plain wide vs LiDAR wide device: no change. Subject-area / smooth AF / face-driven AF+AE / low-light boost all off: scan still runs in low light. isFaceDrivenAutoFocusEnabled = true (steer scan toward faces): worse — scans in daylight too. Red-eye reduction, fusion, distortion correction off: no change. autoFocusRangeRestriction = .near: still converges far. photoQualityPrioritization = .speed everywhere: no change. Prepared photo settings (flash RAW): no change. Re-lock lens mid-scan: fights the scan; worse convergence. Re-lock lens after scan, before strobe: succeeds; exposure still at the scan's position. Torch lit at request time: scans even in bright daylight. The last two items seem diagnostic: the gate is not scene brightness. A torch adding no visible light in daylight still arms the scan, and face-driven AF arms it in bright scenes — the AF-assist stage runs whenever the sequence sees any reason to focus, and a client's .locked focus mode is never treated as that reason being absent. Questions Is the flash AF-assist stage skippable when focusMode == .locked? The earlier thread's DTS reply achieved a locked-focus flash photo — what configuration makes that true on current hardware/iOS? If not skippable: is there a supported way for the final exposure to honor the locked lens position — run the scan but not apply its result? Why does a successful setFocusModeLocked(lensPosition:) issued between scan end and strobe not stick? Is the sequence re-asserting its own focus at exposure time? Are the observed triggers (torch active at request; face-driven AF enabled — each arming the scan regardless of brightness) expected for flashMode = .on? Goal: full-power flash still + fixed focus + manual exposure, which flashMode = .off already delivers minus the flash. Any guidance — including "file a feedback, here's the rdar to duplicate" — appreciated. Happy to attach a focused sample project and sysdiagnose.
0
0
227
2d
Turning AutoFocus off while flash is on.
Hello, I am creating a simple camere app where I want to turn OFF the autofocus (and set the focus within the app) then take a picture with the flash. The only problem is as soon as i set the flash and take a photo, it auto focuses even though i have set the focus mode locked and lens position? Is this even possible? My av capture output settings:     photoOutput.isHighResolutionCaptureEnabled = true     photoOutput.isLivePhotoCaptureEnabled = false;     photoOutput.isDepthDataDeliveryEnabled = false;     photoOutput.isPortraitEffectsMatteDeliveryEnabled = false     photoOutput.isAppleProRAWEnabled = false;     photoOutput.setPreparedPhotoSettingsArray([buildPhotoSettings(flash: flash)]); my AVCapturePhoto Settings     let photoSettings = AVCapturePhotoSettings(rawPixelFormatType: availbleRaw[0]);     photoSettings.isAutoStillImageStabilizationEnabled = false;     photoSettings.flashMode = flash ?.on :.off;     photoSettings.isHighResolutionPhotoEnabled = true;     photoSettings.isAutoRedEyeReductionEnabled = false; my device input settings           //--white balance     let whiteBalanceValues = AVCaptureDevice.WhiteBalanceTemperatureAndTintValues(temperature: settings.whiteBalanceTemp, tint: settings.whiteBalanceTint);     let newWhiteBalance = videoDeviceInput.device.deviceWhiteBalanceGains(for:whiteBalanceValues);     let maxGain = videoDeviceInput.device.maxWhiteBalanceGain           if(newWhiteBalance.redGain > maxGain || newWhiteBalance.greenGain > maxGain || newWhiteBalance.blueGain > maxGain){       videoDeviceInput.device.unlockForConfiguration();       return (false, "WhiteBalance values are invalid (over maximum gain allowed)");     }           videoDeviceInput.device.setWhiteBalanceModeLocked(with: newWhiteBalance);           //--iso and exposure     let exposure = Float64(settings.exposure/1000);     let exposureTime = CMTime(seconds: exposure, preferredTimescale: 1000)     let iso = Float(settings.iso)           if(exposureTime > videoDeviceInput.device.activeFormat.maxExposureDuration || exposureTime < videoDeviceInput.device.activeFormat.minExposureDuration){       videoDeviceInput.device.unlockForConfiguration();       return (false, "Exposure out of bounds");     }           if(iso > videoDeviceInput.device.activeFormat.maxISO || iso < videoDeviceInput.device.activeFormat.minISO){       videoDeviceInput.device.unlockForConfiguration();       return (false, "ISO out of bounds");     }           videoDeviceInput.device.setExposureModeCustom(duration: exposureTime, iso: iso);           //--Lens focus     if(settings.lensPosition < 0 || settings.lensPosition>1){       videoDeviceInput.device.unlockForConfiguration();       return (false, "Lens position out of bounds");     }               if(videoDeviceInput.device.isFocusModeSupported(.locked)){       videoDeviceInput.device.focusMode = .locked;       videoDeviceInput.device.setFocusModeLocked(lensPosition: settings.lensPosition);             }           if #available(iOS 15.4, *) {       if(videoDeviceInput.device.isFaceDrivenAutoFocusEnabled){         videoDeviceInput.device.automaticallyAdjustsFaceDrivenAutoFocusEnabled = false;                 }               if(videoDeviceInput.device.isFaceDrivenAutoExposureEnabled){         videoDeviceInput.device.automaticallyAdjustsFaceDrivenAutoExposureEnabled = false;       }               if(videoDeviceInput.device.isLowLightBoostSupported){         videoDeviceInput.device.automaticallyEnablesLowLightBoostWhenAvailable = false;       }     } else {       // Fallback on earlier versions     }           //--Torch ON     videoDeviceInput.device.torchMode = settings.torch ? AVCaptureDevice.TorchMode.on :AVCaptureDevice.TorchMode.off;           if(settings.torch){       if(settings.torchLevel<0 || settings.torchLevel>1){         return (false, "Flash out of bounds");       }               try? videoDeviceInput.device.setTorchModeOn(level: Float(settings.torchLevel));     }                       //--Finish     videoDeviceInput.device.unlockForConfiguration();
3
0
1.6k
2d
Supported Core Image workflow for cropping/scaling Apple Log x422 buffers without converting to HLG?
We capture Apple Log video using AVCaptureVideoDataOutput with: AVCaptureDevice.activeColorSpace = .appleLog or .appleLog2 kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange (x422) AVAssetWriter for the final ProRes/HEVC recording We need to bake spatial operations such as a centre crop and optional anamorphic desqueeze into the recorded raster. We want the result to remain Apple Log for subsequent grading; we do not want to display-transform or convert it to HLG. Core Image/Core Graphics does not appear to expose a public Apple Log or Apple Log 2 CGColorSpace. Core Video instead identifies the signal using kCVImageBufferLogTransferFunctionKey. Would this be the supported Core Image approach for spatial-only processing? let image = CIImage( cvPixelBuffer: sourceBuffer, options: [.colorSpace: NSNull()] ) let context = CIContext( mtlDevice: metalDevice, options: [ .workingColorSpace: NSNull(), .outputColorSpace: NSNull(), .cacheIntermediates: false ] ) let outputImage = image .cropped(to: cropRect) .transformed(by: spatialTransform) context.render( outputImage, to: destinationX422Buffer, bounds: outputBounds, colorSpace: nil ) We would then copy the source buffer’s Apple Log colour and Log-transfer attachments to the destination buffer and append it to an AVAssetWriterInputPixelBufferAdaptor. Could an Apple engineer clarify the following? Does CIImage(cvPixelBuffer:) natively interpret kCVImageBufferLogTransferFunctionKey, even though no public Apple Log CGColorSpace exists? When NSNull()/nil is supplied as above, does Core Image leave the Apple Log values unmanaged during crop and affine operations? When rendering x422 to x422, can Core Image preserve the Log signal apart from expected resampling/rounding, or does it internally perform a colour conversion such as YCbCr → RGB → YCbCr? For scaling or anamorphic desqueeze, is Apple’s recommended workflow to: resample the encoded Apple Log values with colour management disabled, or explicitly decode Apple Log to a linear working space, resample, and encode back using a custom Metal implementation? Is copying kCVImageBufferLogTransferFunctionKey and the related colour attachments from source to destination sufficient for AVAssetWriter, assuming the writer settings were obtained from recommendedVideoSettingsForAssetWriter(writingTo:)? Is there ever a supported reason to use HLG as an intermediate for Apple Log processing, or should that be avoided? This this part, we are concerned about preserving the captured Log signal in a recording pipeline, not applying a viewing LUT or displaying Apple Log. Next, we also support optional LUT processing with CIColorCube. Some LUTs are technical Apple Log-to-display transforms, while others expect linear or Rec.709 input. If the image is created with .colorSpace: NSNull(), we understand that the LUT receives unmanaged Apple Log code values and that the LUT itself must perform the required transfer-function and gamut conversion. Does Core Image synthesize an Apple Log-aware input color space from kCVImageBufferLogTransferFunctionKey, or must applications implement Apple Log/Apple Log 2 decoding explicitly before using ordinary Core Image filters? Is there a supported CGColorSpace, ColorSync profile, or Core Image conversion API for this? Additionally, what numeric range does CIColorCube receive when its source is an x422 video-range Apple Log buffer— normalized Log RGB values, or values requiring explicit video-range conversion?
0
0
283
3d
Supported way to re-acquire genlock after follow() detaches mid-session?
Summary. iPhone 17 Pro Max + Blackmagic Camera ProDock, genlock BNC in from a generator confirmed at a true 30.00 fps. follow(_:videoFrameDuration:delegate:) reaches .activeSync in ~2 s. On one unit the lock then holds for 10+ minutes. On a second unit, same binary and same reference, it detaches 6 s to ~4 min after lock: .activeSync → .ready with input.externalSyncDevice == nil, no runtime error and no delegate error. Calling follow() again on the still-running session is rejected with -11800 every time, while unfollowExternalSyncDevice() plus a stopRunning()/startRunning() bounce recovers reliably. I am not looking for a fix. I want to know whether my call sequence is wrong, whether this transition is expected, and how a shipping app should be structured around it. Questions Is re-following a running session supported? Is the unfollow + bounce the intended reset sequence, or is there a lighter-weight way to clear whatever state the -11800 is keyed on? And once detached, is re-acquisition entirely the app's responsibility, or is the system expected to re-calibrate on its own while the reference is present? Is a hard detach a legal outcome for an already-calibrated input? The documentation describes .freeRunSync as the hold-over when a locked input loses sync. Is that hold-over guaranteed, or must an app also handle .activeSync → .ready with a nil externalSyncDevice? Is anything in my call sequence wrong (code in a reply below), and is polling input.externalSyncDevice the right signal to key recovery on, or is there a supported notification for detach? Setup. Video-only AVCaptureSession: one .builtInWideAngleCamera input, one AVCaptureVideoDataOutput. No multi-cam, no depth output, no synchronizer, no audio. Both frame durations set to CMTime(1, 30) before the input is created, never rewritten while a follow is live. Device A (iOS 26.0.1) holds: 601 s and 956 s runs, zero detaches. Device B (26.3.1, then 26.5.2) has 39 drops across 6 logs. The ProDock, cable and generator were swapped between units; the failure followed the phone. Caveat: n=2, and unit and OS build vary together. The detach. No AVCaptureSessionRuntimeError and no delegate error (-11892 has never been observed here, so this is not the documented frame-duration-mismatch path). The session keeps running and delivering frames. Status is .ready, not .unavailable — the ProDock stays enumerated, the reference unchanged. No confirmed drop has passed through .freeRunSync. PTS ground truth, independent of the follow state: while locked, every frame PTS sits exactly on the 1/30 grid, zero drift. At the drop there is exactly one teardown gap, 238–337 ms across 8 runs, after which the clock free-runs at ~30.013 fps (444 ppm) and never returns to the grid. On -11800. It surfaces through AVCaptureSessionRuntimeErrorNotification. The bounce recovers 3/3, with no activeFormat change. Caveat: -11800 is AVErrorUnknown and I see it in unrelated cases too, so I do not assume it is specific to retained follow state. Ruled out. Exposure duration — a run at ≤ 16.67 ms, within the recommendation in the follow() documentation, still drops. Reference drift — zero, by microsecond PTS. Accessory chain — full swap; the failure followed the phone. Another client — Final Cut Camera holds genlock on the fragile unit with the same ProDock and reference. Load and resolution — load changes time-to-drop, not whether it drops; with recording and audio off it still drops, and Device B drops at both 12 MP and 1080p. Code and supporting logs are in replies below; the length limit would not take them inline. Per-frame PTS CSVs, raw status logs, and a minimal Xcode project that still drops are available on request. Prior art read: forums/thread/799739 and thread/804594 — neither covers post-lock detach or re-acquisition.
3
0
713
4d
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
569
1w
Programmatic / Background Trigger for ReplayKit Broadcast (Without User Intervention)
Hi everyone, I am working on an iOS application that utilizes a Broadcast Upload Extension (ReplayKit) to perform local, on-device screen analysis. Currently, we are using RPSystemBroadcastPickerView to allow the user to initiate the broadcast session. However, for our specific tracking use case, requiring the user to manually tap the "Start Broadcast" button every time creates a significant friction point in the user experience. My questions are: Is there any private API, entitlement, or MDM (Mobile Device Management) configuration that allows an app to programmatically start a ReplayKit screen recording session completely in the background without explicit human intervention (e.g., without tapping a button in the UI)? If this is strictly prohibited for consumer apps on the App Store due to privacy guidelines, are there any exceptions or enterprise-level profiles available for supervised devices that bypass the mandatory RPSystemBroadcastPickerView user interaction? My understanding is that Apple enforces this manual trigger and the red status bar indicator for strict security and privacy reasons, but I am looking for an official confirmation on whether any programmatic workaround exists for this in modern iOS versions (iOS 15+). Thank you in advance for your time and clarification!
1
0
265
1w
CarPlay: Is vehicle microphone capture without entering communications audio mode supported for third-party apps?
Hello, I am developing a native CarPlay application and would appreciate some clarification regarding AVAudioSession behaviour when using the vehicle microphone. The application is intentionally simple and designed to minimise driver distraction. The user presses a single button on the CarPlay screen, the application performs a brief (approximately five second) audio capture, performs application-specific processing on the captured audio, displays a simple confirmation to the user, and immediately releases the audio session. The goal is to allow the driver to continue their journey with as little interaction as possible. Because the audio being captured is often originating from the vehicle’s native DAB/FM radio rather than from the iPhone itself, preserving the existing listening experience during the brief capture is fundamental to the intended design. To better understand what is supported by CarPlay, I have carried out a number of controlled experiments. Test Environment Native CarPlay application Swift AVFoundation AVAudioSession AVAudioRecorder Vehicle connected via CarPlay Vehicle playing its native DAB radio Experiment 1 Configuration Category: AVAudioSession.Category.playAndRecord Mode: AVAudioSession.Mode.default Observed Route Input: CarPlay — CarAudio Output: CarPlay — CarAudio Result Recording succeeds using the vehicle microphone. Native DAB audio is muted during recording. The vehicle changes from “Audio Volume” to “Voice Volume”. When recording finishes and the AVAudioSession is deactivated, DAB resumes normally. Experiment 2 Changed only the session mode to: AVAudioSession.Mode.measurement Result Behaviour was identical to Experiment 1. Experiment 3 Changed only the session mode to: AVAudioSession.Mode.videoRecording Observed Route Input: iPhone microphone Output: CarPlay — CarAudio Result Input automatically switched from the vehicle microphone to the iPhone microphone. The vehicle remained in normal “Audio Volume”. However, the head unit switched away from its native DAB source to the CarPlay media source. Observation These experiments appear to suggest that the behaviour is specifically related to using the CarPlay “CarAudio” microphone route, rather than audio recording itself. Question Is this the expected behaviour for third-party CarPlay applications? More specifically: Is there any supported AVAudioSession configuration or CarPlay API that allows a third-party CarPlay application to perform a brief, user-initiated audio capture using the vehicle microphone without causing the head unit to enter its communications/voice audio mode or interrupt the vehicle’s native radio playback? If this behaviour is by design and no supported mechanism exists to achieve this, confirmation of that would be extremely valuable, as it would allow us to design the application accordingly. If additional information, sample code, AVAudioSession logs or detailed reproduction steps would be helpful, I would be more than happy to provide them. Thank you very much for your time. Kind regards, Neil Jenner Developer, HearSave
7
0
622
2w
ProResRAW shooting issue with AVCaptureMovieFileOutput with the first video
On iOS 26, the first ProRes RAW recording after launching the app consistently stalls for the entire take: audio records normally, but video contains only a few frames (for example, an 11.8-second clip at approximately 0.25 fps instead of 30 fps). Every subsequent recording works correctly. Before startRecording, the Bayer format is active, ProRes RAW is available, white balance is locked, and frame duration is pinned to 1/30. Reordering setOutputSettings and reasserting configuration have not resolved it; only discarding the first recording acts as a reliable warm-up. Is this a known one-time ProRes RAW encoder initialisation issue, and is there a supported way to prepare the encoder before the first user takes? I have a full working sample code if anyone needs but here is the setup function: import CoreMedia import CoreVideo @available(iOS 26.0, *) final class ProResRAWRecorder: NSObject, AVCaptureFileOutputRecordingDelegate { let session = AVCaptureSession() private let movieOutput = AVCaptureMovieFileOutput() private var camera: AVCaptureDevice! // Call on a serial capture queue. func configure() throws { session.beginConfiguration() defer { session.commitConfiguration() } session.sessionPreset = .inputPriority session.automaticallyConfiguresCaptureDeviceForWideColor = false guard let device = AVCaptureDevice.default( .builtInWideAngleCamera, for: .video, position: .back ) else { throw SampleError.noCamera } let cameraInput = try AVCaptureDeviceInput(device: device) guard session.canAddInput(cameraInput) else { throw SampleError.cannotAddInput } session.addInput(cameraInput) camera = device // Optional, but reproduces the real topology where audio remains healthy. if let microphone = AVCaptureDevice.default(for: .audio), let microphoneInput = try? AVCaptureDeviceInput(device: microphone), session.canAddInput(microphoneInput) { session.addInput(microphoneInput) } // Find a 12-bit packed Bayer format supporting 30 fps. guard let rawFormat = device.formats .filter({ CMFormatDescriptionGetMediaSubType($0.formatDescription) == kCVPixelFormatType_96VersatileBayerPacked12 && $0.videoSupportedFrameRateRanges.contains { $0.minFrameRate <= 30 && $0.maxFrameRate >= 30 } }) .max(by: { let a = CMVideoFormatDescriptionGetDimensions($0.formatDescription) let b = CMVideoFormatDescriptionGetDimensions($1.formatDescription) return Int(a.width) * Int(a.height) < Int(b.width) * Int(b.height) }) else { throw SampleError.noRAWFormat } try device.lockForConfiguration() device.activeFormat = rawFormat if rawFormat.supportedColorSpaces.contains(.appleLog) { device.activeColorSpace = .appleLog } let frameDuration = CMTime(value: 1, timescale: 30) device.activeVideoMinFrameDuration = frameDuration device.activeVideoMaxFrameDuration = frameDuration if device.isWhiteBalanceModeSupported(.locked) { device.whiteBalanceMode = .locked } device.unlockForConfiguration() guard session.canAddOutput(movieOutput) else { throw SampleError.cannotAddOutput } session.addOutput(movieOutput) session.startRunning() } // Call on the same serial capture queue after startRunning() returns. func record(to url: URL) throws { guard let connection = movieOutput.connection(with: .video) else { throw SampleError.noVideoConnection } // Reassert the required device state at the take boundary. try camera.lockForConfiguration() if camera.isWhiteBalanceModeSupported(.locked) { camera.whiteBalanceMode = .locked } let frameDuration = CMTime(value: 1, timescale: 30) camera.activeVideoMinFrameDuration = frameDuration camera.activeVideoMaxFrameDuration = frameDuration camera.unlockForConfiguration() guard movieOutput.availableVideoCodecTypes.contains(.proResRAW) else { throw SampleError.rawCodecUnavailable } movieOutput.setOutputSettings( [AVVideoCodecKey: AVVideoCodecType.proResRAW], for: connection ) movieOutput.startRecording(to: url, recordingDelegate: self) } func stop() { if movieOutput.isRecording { movieOutput.stopRecording() } } func fileOutput( _ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error? ) { print("Finished:", outputFileURL, "error:", error as Any) } enum SampleError: Error { case noCamera case cannotAddInput case noRAWFormat case cannotAddOutput case noVideoConnection case rawCodecUnavailable } }
1
0
1.1k
2w
Manual legible (subtitle) selection not honored on live LL-HLS — select(_:in:) reverts to “off” within ~2s; automatic selection never displays non-forced subtitles
Environment: iOS 18 / iOS 26, AVPlayer + AVPlayerItem, live low-latency HLS (LL-HLS). Subtitle renditions are regular (non-forced) WebVTT: AUTOSELECT=YES, FORCED=NO. System captioning (Closed Captions + SDH) is OFF (default). Direct CDN, no P2P. Master playlist (subtitle part): #EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Korean (AI)",AUTOSELECT=YES,FORCED=NO,LANGUAGE="kor",URI="..." #EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English (AI)",AUTOSELECT=YES,FORCED=NO,LANGUAGE="eng",URI="..." #EXT-X-STREAM-INF:...,SUBTITLES="subs" Problem When the user picks a subtitle language we call playerItem.select(option, in: legibleGroup) with an option that is a genuine member of the item's current legible group. Right after the call, item.currentMediaSelection.selectedMediaOption(in:) returns the requested option. But within ~2 seconds, on a subset of real-user sessions, the selection spontaneously reverts with no further app interaction: most often to Off (no legible output is delivered afterwards), or it stays stuck on the previously selected language (a subsequent select to another language, or to Off, is silently ignored). It is intermittent and only appears under real conditions (frequent live playlist reloads / reconnects); it does not reproduce in a short, clean session. What we verified The option passed to select(_:in:) is a real member of the current group (re-resolved from item.asset); the call returns without error and reads back correctly immediately after. appliesMediaSelectionCriteriaAutomatically is the default true. Per the AV Foundation Release Notes ("Advice about subtitles"), automatic selection excludes options that are not AVMediaCharacteristicContainsOnlyForcedSubtitles. So on each live reload the automatic re-selection resolves non-forced subtitles to Off, appearing to override the manual select(_:in:). Reading the selected option against a freshly obtained group instance returns the same value as against the original instance — so this is a real state change, not a mismatched-group read. Questions On live HLS reloads with appliesMediaSelectionCriteriaAutomatically == true, is manual select(_:in:) for .legible expected to be overridden by automatic media selection? If so, is setMediaSelectionCriteria(_:forMediaCharacteristic:) the intended way to persist a user's choice? setMediaSelectionCriteria for subtitles is itself reported as unreliable (sometimes no subtitles) — see thread 108403. What is the recommended, deterministic way to keep a user-selected non-forced subtitle displayed across live playlist reloads, including turning subtitles Off? Is this the same underlying behavior as FB13344652 ("Auto (Recommended) doesn't display subtitles despite language match / DEFAULT=YES")? Related: https://developer.apple.com/forums/thread/722752 (FB13344652) , https://developer.apple.com/forums/thread/108403
1
0
352
3w
iOS 27 beta: Opening Notification Center pauses AVPlayer playback
Since iOS 27 beta we are seeing a behavior change in our video streaming app and I would like to know whether others can reproduce it. Behavior on iOS 27 beta: Fully opening the Notification Center pauses playback. Audio continues for about 5 more seconds, then the app is suspended. Closing the Notification Center leaves the player paused. On iOS 26 the same build keeps playing in this situation. Setup: AVQueuePlayer playing video with audio, not muted audiovisualBackgroundPlaybackPolicy = .automatic (default) AVAudioSession category .playback, UIBackgroundModes: audio entitlement AVPictureInPictureController attached with canStartPictureInPictureAutomaticallyFromInline = true Filed as FB23893965 Questions: Can anyone reproduce this on iOS 27 beta? Is this intentional or a regression?
0
2
330
3w
Add a value to the Photos Caption field
In the iOS Photos app there is a caption field the user can write to. How can you write to this value from Swift when creating a photo? I see apps that do this, but there doesn't seem to be any official way to do this using the Photo library through PHAssetCreationRequest or PHAssetResourceCreationOptions or setting EXIF values, I tried settings a bunch of values there including IPTC values but nothing appears in the caption field in the iOS photos app. There must be some way to do it since I see other apps setting that value somehow after capturing a photo.
2
1
551
3w
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
477
3w
How should live latency be measured and maintained with AVPlayer (HLS / LL-HLS)?
We keep live playback at a consistent distance from the live edge using small playback rate adjustments, with a target range based on recommendedTimeOffsetFromLive. Since the live edge is not exposed by AVPlayer, we currently fall back to seekableTimeRanges.end as our best approximation. What should be treated as the live edge, and how should the current live latency be measured? Is rate adjustment the appropriate way to hold a target latency? While playing above 1.0x, the playhead can reach the seekable end, at which point AVPlayerItemDidPlayToEndTime fires and halts the live stream. How can we guard against ? Does any of this differ between regular HLS and LL-HLS? A clear statement of the intended contract here would resolve a lot of uncertainty. Thanks in advance.
2
0
463
4w
`LockedCameraCaptureManager` practically unusable since iOS 26
Somewhere since iOS 26, the LockedCameraCapture framework gets in an unpredictable state after opening the main app from the LockedCamera extension using LockedCameraCaptureSession.openApplication(for userActivity:). (Feedback with sample code to reproduce: FB21966835) Opening the extension from the lock screen again doesn’t open the extension but puts the lock screen in a state as if it has. Content updated from LockedCameraCaptureManager.shared.sessionContentUpdates comes in inconsistently, usually needs the app to be opened again or the extension to be opened. This makes using this extension impossible for me as I use it to record video files that manually need to be imported when the app is launched (so not through PhotoKit). Does anybody have a suggestion to circumvent this issue or how to get this fixed?
1
2
846
4w
iOS 26.4 regression: The `.pauses` audiovisual background playback policy does not pause video playback anymore when backgrounding the app
Starting with iOS 26.4 and the iOS 26.4 SDK, the .pauses audiovisual background playback policy is not correctly applied anymore to an AVPlayer having an attached video layer displayed on screen. This means that, when backgrounding a video-playing app (without Picture in Picture support) or locking the device, playback is not paused automatically by the system anymore. This issue affects the Apple TV application as well. We have filed FB22488151 with more information.
2
0
919
Jul ’26
Does the TN3135 audio-session networking exception have a defined lifetime? Seeing a ~38.5 s revoke/re-grant cycle
TN3135 describes the exception that lets a watchOS app use low-level networking while it holds an active audio session. I have that working, and the app functions — but the network path is withdrawn and restored on a strikingly regular cycle, and I would like to know whether that is expected behaviour rather than something I am doing wrong. Setup Apple Watch Series 10 (Watch7,9), watchOS 26.5. Reproduced on a Series 6 (Watch6,2). UIBackgroundModes: [audio]; AVAudioSession category .playAndRecord, mode .spokenAudio; activated with the async activate(options:completionHandler:). NWConnection with NWProtocolWebSocket to a WebSocket relay over TLS. The app streams 16 kHz mono PCM continuously while transmitting and holds the socket open otherwise. Symptom NWPathMonitor reports .unsatisfied, then .satisfied about two seconds later, over and over. Measured with the iPhone powered off, so the watch was on its own Wi-Fi: Uptime between drops Outage 36.4 s 2.1 s 36.7 s 1.9 s 36.9 s 2.1 s The regularity is what prompts the question — uptime varies by ±0.3 s and the outage is consistently 2.0 s. That reads as a timeout expiring rather than radio behaviour. What I have ruled out Not the network or the server. A browser client on the same relay, same TLS, same wire protocol, holds a WebSocket indefinitely. Not the interface. Identical cadence over the companion ipsec1 tunnel with the iPhone present, and over the watch's own en0 with the iPhone powered off. Pinning requiredInterfaceType = .wifi while the iPhone is reachable fails outright — the path offers only ipsec1. Not audio-session interruption. I observe interruptionNotification, routeChangeNotification, mediaServicesWereResetNotification and silenceSecondaryAudioHintNotification. None fire at a drop. At the moment the path goes .unsatisfied, the engine is running and the player node is actively playing. Not session idleness. Playing continuous silence for the whole session, rather than only while reconnecting, made no difference — still 36.4 s. The control that surprised me To test whether this affects any long-lived watch socket or only audio-unlocked ones, I built a second app with no AVAudioSession at all, no audio background mode, holding a URLSessionWebSocketTask and kept alive by a WKExtendedRuntimeSession so screen sleep was not a factor. It never connected. NWPathMonitor reported .unsatisfied once and never changed, across a 30 s run, and every request failed with "The Internet connection appears to be offline." I had expected URLSession to be permitted regardless. Questions Does the audio-session networking exception in TN3135 have a defined lifetime, and is a periodic revoke/re-grant cycle expected? If so, is there a supported way to hold it continuously — or is the correct design simply to expect the interruption and reconnect through it? Is it expected that an app with no audio session gets no network path at all on watchOS, including via URLSession, even in the foreground with an extended runtime session?
Replies
1
Boosts
0
Views
48
Activity
18h
AVPlayerItemSampleBufferOutput
I am using AVPlayer, but I am unable to retrieve PCM data from .m3u8 streams via the AVPlayerItemSampleBufferOutput API in iOS 27 beta. I have tried both the Objective-C and Swift APIs: the Objective-C delegate methods are not being called, and the Swift methods nextAvailableSampleBuffer() and nextSampleBuffer() are returning no data.
Replies
0
Boosts
0
Views
24
Activity
22h
fail to get HLS realtime stream via AVPlayerItemSampleBufferOutputDelegate
Hi, 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). And I find that new APIs(AVPlayerItemSampleBufferOutput) are available in iOS 27.0 to achieve it. However, I failed to get the available data via the AVPlayerItemSampleBufferOutputDelegate. And I found some error in the system log: I create AVPlayerItemSampleBufferOutput and set the delegate after receiving the AVPlayerItemStatusReadyToPlay event. And here's my code: @interface OCAudioSamplebuffer () <AVPlayerItemSampleBufferOutputDelegate> @property (nonatomic, strong) AVPlayerItemSampleBufferOutput *bufferOutput; @property (nonatomic, strong) dispatch_queue_t bufferOutputQueue; @property (nonatomic, strong) AVPlayerItem *playerItem; @end - (void)playItem:(AVPlayerItem *)item { if (@available(iOS 27, *)) { AVAudioSession *audioSession = [AVAudioSession sharedInstance]; if([NSThread mainThread]){ [audioSession setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryPlayback error:nil]; [audioSession setActive:YES error:nil]; }else{ dispatch_async(dispatch_get_main_queue(), ^{ [audioSession setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryPlayback error:nil]; [audioSession setActive:YES error:nil]; }); } AVPlayerItemSampleBufferOutputAudioConfiguration *cfg = [[AVPlayerItemSampleBufferOutputAudioConfiguration alloc] init]; CMFormatDescriptionRef formatDescription = [self createPCMFormatDescriptionWithSampleRate:44100.0 channels:2 isFloat:YES]; cfg.requestedAudioFormat = formatDescription; if (formatDescription) { NSLog(@"create PCM Format Description success"); CFRelease(formatDescription); } else { NSLog(@"fail to create PCM Format Description"); } self.bufferOutput = [[AVPlayerItemSampleBufferOutput alloc] initWithConfiguration:cfg]; NSLog(@"create buffer success, PCM Format Description%@---,%@", cfg.requestedAudioFormat, formatDescription); self.bufferOutputQueue = dispatch_queue_create("audioSamplebufferQueue", DISPATCH_QUEUE_CONCURRENT); [self.bufferOutput setDelegate:self queue:self.bufferOutputQueue]; [item addOutput:self.bufferOutput]; self.playerItem = item; } else { // Fallback on earlier versions } } Any help. Thank you
Replies
1
Boosts
0
Views
346
Activity
1d
flashMode .on overrides locked focus — AF scan runs and refocuses before the strobe
I'm building a fixed-focus camera app (Bayer RAW, manual exposure, focus permanently locked at a known lens position) and need a full-power flash still that keeps that locked focus. On iPhone 17 Pro, iOS 26, any capture with flashMode = .on runs an autofocus-assist scan — visible lens hunt, assist lamp in low light — and the exposure happens at whatever the scan converged on (usually the far background), not my locked position. In low light it reproduces every time; bright-scene behavior varied by configuration (see matrix), and in my current build it hunts in daylight too. This follows https://developer.apple.com/forums/thread/724897 where DTS confirmed locked lens position + flash "is possible" in a test app, but the differentiating configuration was never identified. I've now exhausted every documented lever, with instrumentation, and would like guidance on whether this stage is bypassable at all. Session configuration session.sessionPreset = .photo // Plain wide camera (also reproduced on .builtInLiDARDepthCamera). let camera = AVCaptureDevice.default( .builtInWideAngleCamera, for: .video, position: .back )! session.addInput(try AVCaptureDeviceInput(device: camera)) session.addOutput(photoOutput) photoOutput.maxPhotoQualityPrioritization = .speed photoOutput.isZeroShutterLagEnabled = false photoOutput.isResponsiveCaptureEnabled = true // Pre-allocate flash capture resources up front. photoOutput.setPreparedPhotoSettingsArray( [makeFlashRawSettings()], completionHandler: nil ) Device configuration (before any capture) try camera.lockForConfiguration() camera.isSubjectAreaChangeMonitoringEnabled = false camera.isSmoothAutoFocusEnabled = false camera.autoFocusRangeRestriction = .near camera.automaticallyEnablesLowLightBoostWhenAvailable = false camera.automaticallyAdjustsFaceDrivenAutoFocusEnabled = false camera.isFaceDrivenAutoFocusEnabled = false // true is worse; see matrix camera.automaticallyAdjustsFaceDrivenAutoExposureEnabled = false camera.isFaceDrivenAutoExposureEnabled = false camera.setFocusModeLocked(lensPosition: 0.60) // ~1 m on this device let gains = camera.deviceWhiteBalanceGains( for: .init(temperature: 4000, tint: 0) ) camera.setWhiteBalanceModeLocked(with: gains) camera.setExposureModeCustom( duration: CMTime(value: 1, timescale: 125), iso: 400 ) camera.unlockForConfiguration() Capture func makeFlashRawSettings() -> AVCapturePhotoSettings { let bayer = photoOutput.availableRawPhotoPixelFormatTypes.first { AVCapturePhotoOutput.isBayerRAWPixelFormat($0) }! let settings = AVCapturePhotoSettings(rawPixelFormatType: bayer) settings.flashMode = .on settings.isAutoRedEyeReductionEnabled = false settings.isAutoStillImageStabilizationEnabled = false settings.isAutoVirtualDeviceFusionEnabled = false settings.isAutoContentAwareDistortionCorrectionEnabled = false settings.photoQualityPrioritization = .speed return settings } // Right before the request, exposure flips .custom → .locked (same // duration/ISO), one preview frame presents, then: photoOutput.capturePhoto(with: makeFlashRawSettings(), delegate: self) What I measure (KVO during the capture window) isAdjustingFocus goes true after the request: a scan runs despite focusMode == .locked (assist lamp on in low light). lensPosition moves from the locked 0.60 to a far position and stays there through willCapturePhotoFor — the exposure uses the scan's answer. When isAdjustingFocus falls back to false (before the metering preflash) I issue one setFocusModeLocked(lensPosition: 0.60). The call succeeds, yet the exposure still happens at the far position — something re-asserts the scan's focus before the strobe. With flashMode = .off, locked focus and custom exposure are honored perfectly (my app's normal path). Tried, all reproduced focusMode = .locked + setFocusModeLocked(lensPosition:): scan still runs. Exposure .custom → .locked for the flash window: no change. Plain wide vs LiDAR wide device: no change. Subject-area / smooth AF / face-driven AF+AE / low-light boost all off: scan still runs in low light. isFaceDrivenAutoFocusEnabled = true (steer scan toward faces): worse — scans in daylight too. Red-eye reduction, fusion, distortion correction off: no change. autoFocusRangeRestriction = .near: still converges far. photoQualityPrioritization = .speed everywhere: no change. Prepared photo settings (flash RAW): no change. Re-lock lens mid-scan: fights the scan; worse convergence. Re-lock lens after scan, before strobe: succeeds; exposure still at the scan's position. Torch lit at request time: scans even in bright daylight. The last two items seem diagnostic: the gate is not scene brightness. A torch adding no visible light in daylight still arms the scan, and face-driven AF arms it in bright scenes — the AF-assist stage runs whenever the sequence sees any reason to focus, and a client's .locked focus mode is never treated as that reason being absent. Questions Is the flash AF-assist stage skippable when focusMode == .locked? The earlier thread's DTS reply achieved a locked-focus flash photo — what configuration makes that true on current hardware/iOS? If not skippable: is there a supported way for the final exposure to honor the locked lens position — run the scan but not apply its result? Why does a successful setFocusModeLocked(lensPosition:) issued between scan end and strobe not stick? Is the sequence re-asserting its own focus at exposure time? Are the observed triggers (torch active at request; face-driven AF enabled — each arming the scan regardless of brightness) expected for flashMode = .on? Goal: full-power flash still + fixed focus + manual exposure, which flashMode = .off already delivers minus the flash. Any guidance — including "file a feedback, here's the rdar to duplicate" — appreciated. Happy to attach a focused sample project and sysdiagnose.
Replies
0
Boosts
0
Views
227
Activity
2d
Turning AutoFocus off while flash is on.
Hello, I am creating a simple camere app where I want to turn OFF the autofocus (and set the focus within the app) then take a picture with the flash. The only problem is as soon as i set the flash and take a photo, it auto focuses even though i have set the focus mode locked and lens position? Is this even possible? My av capture output settings:     photoOutput.isHighResolutionCaptureEnabled = true     photoOutput.isLivePhotoCaptureEnabled = false;     photoOutput.isDepthDataDeliveryEnabled = false;     photoOutput.isPortraitEffectsMatteDeliveryEnabled = false     photoOutput.isAppleProRAWEnabled = false;     photoOutput.setPreparedPhotoSettingsArray([buildPhotoSettings(flash: flash)]); my AVCapturePhoto Settings     let photoSettings = AVCapturePhotoSettings(rawPixelFormatType: availbleRaw[0]);     photoSettings.isAutoStillImageStabilizationEnabled = false;     photoSettings.flashMode = flash ?.on :.off;     photoSettings.isHighResolutionPhotoEnabled = true;     photoSettings.isAutoRedEyeReductionEnabled = false; my device input settings           //--white balance     let whiteBalanceValues = AVCaptureDevice.WhiteBalanceTemperatureAndTintValues(temperature: settings.whiteBalanceTemp, tint: settings.whiteBalanceTint);     let newWhiteBalance = videoDeviceInput.device.deviceWhiteBalanceGains(for:whiteBalanceValues);     let maxGain = videoDeviceInput.device.maxWhiteBalanceGain           if(newWhiteBalance.redGain > maxGain || newWhiteBalance.greenGain > maxGain || newWhiteBalance.blueGain > maxGain){       videoDeviceInput.device.unlockForConfiguration();       return (false, "WhiteBalance values are invalid (over maximum gain allowed)");     }           videoDeviceInput.device.setWhiteBalanceModeLocked(with: newWhiteBalance);           //--iso and exposure     let exposure = Float64(settings.exposure/1000);     let exposureTime = CMTime(seconds: exposure, preferredTimescale: 1000)     let iso = Float(settings.iso)           if(exposureTime > videoDeviceInput.device.activeFormat.maxExposureDuration || exposureTime < videoDeviceInput.device.activeFormat.minExposureDuration){       videoDeviceInput.device.unlockForConfiguration();       return (false, "Exposure out of bounds");     }           if(iso > videoDeviceInput.device.activeFormat.maxISO || iso < videoDeviceInput.device.activeFormat.minISO){       videoDeviceInput.device.unlockForConfiguration();       return (false, "ISO out of bounds");     }           videoDeviceInput.device.setExposureModeCustom(duration: exposureTime, iso: iso);           //--Lens focus     if(settings.lensPosition < 0 || settings.lensPosition>1){       videoDeviceInput.device.unlockForConfiguration();       return (false, "Lens position out of bounds");     }               if(videoDeviceInput.device.isFocusModeSupported(.locked)){       videoDeviceInput.device.focusMode = .locked;       videoDeviceInput.device.setFocusModeLocked(lensPosition: settings.lensPosition);             }           if #available(iOS 15.4, *) {       if(videoDeviceInput.device.isFaceDrivenAutoFocusEnabled){         videoDeviceInput.device.automaticallyAdjustsFaceDrivenAutoFocusEnabled = false;                 }               if(videoDeviceInput.device.isFaceDrivenAutoExposureEnabled){         videoDeviceInput.device.automaticallyAdjustsFaceDrivenAutoExposureEnabled = false;       }               if(videoDeviceInput.device.isLowLightBoostSupported){         videoDeviceInput.device.automaticallyEnablesLowLightBoostWhenAvailable = false;       }     } else {       // Fallback on earlier versions     }           //--Torch ON     videoDeviceInput.device.torchMode = settings.torch ? AVCaptureDevice.TorchMode.on :AVCaptureDevice.TorchMode.off;           if(settings.torch){       if(settings.torchLevel<0 || settings.torchLevel>1){         return (false, "Flash out of bounds");       }               try? videoDeviceInput.device.setTorchModeOn(level: Float(settings.torchLevel));     }                       //--Finish     videoDeviceInput.device.unlockForConfiguration();
Replies
3
Boosts
0
Views
1.6k
Activity
2d
Supported Core Image workflow for cropping/scaling Apple Log x422 buffers without converting to HLG?
We capture Apple Log video using AVCaptureVideoDataOutput with: AVCaptureDevice.activeColorSpace = .appleLog or .appleLog2 kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange (x422) AVAssetWriter for the final ProRes/HEVC recording We need to bake spatial operations such as a centre crop and optional anamorphic desqueeze into the recorded raster. We want the result to remain Apple Log for subsequent grading; we do not want to display-transform or convert it to HLG. Core Image/Core Graphics does not appear to expose a public Apple Log or Apple Log 2 CGColorSpace. Core Video instead identifies the signal using kCVImageBufferLogTransferFunctionKey. Would this be the supported Core Image approach for spatial-only processing? let image = CIImage( cvPixelBuffer: sourceBuffer, options: [.colorSpace: NSNull()] ) let context = CIContext( mtlDevice: metalDevice, options: [ .workingColorSpace: NSNull(), .outputColorSpace: NSNull(), .cacheIntermediates: false ] ) let outputImage = image .cropped(to: cropRect) .transformed(by: spatialTransform) context.render( outputImage, to: destinationX422Buffer, bounds: outputBounds, colorSpace: nil ) We would then copy the source buffer’s Apple Log colour and Log-transfer attachments to the destination buffer and append it to an AVAssetWriterInputPixelBufferAdaptor. Could an Apple engineer clarify the following? Does CIImage(cvPixelBuffer:) natively interpret kCVImageBufferLogTransferFunctionKey, even though no public Apple Log CGColorSpace exists? When NSNull()/nil is supplied as above, does Core Image leave the Apple Log values unmanaged during crop and affine operations? When rendering x422 to x422, can Core Image preserve the Log signal apart from expected resampling/rounding, or does it internally perform a colour conversion such as YCbCr → RGB → YCbCr? For scaling or anamorphic desqueeze, is Apple’s recommended workflow to: resample the encoded Apple Log values with colour management disabled, or explicitly decode Apple Log to a linear working space, resample, and encode back using a custom Metal implementation? Is copying kCVImageBufferLogTransferFunctionKey and the related colour attachments from source to destination sufficient for AVAssetWriter, assuming the writer settings were obtained from recommendedVideoSettingsForAssetWriter(writingTo:)? Is there ever a supported reason to use HLG as an intermediate for Apple Log processing, or should that be avoided? This this part, we are concerned about preserving the captured Log signal in a recording pipeline, not applying a viewing LUT or displaying Apple Log. Next, we also support optional LUT processing with CIColorCube. Some LUTs are technical Apple Log-to-display transforms, while others expect linear or Rec.709 input. If the image is created with .colorSpace: NSNull(), we understand that the LUT receives unmanaged Apple Log code values and that the LUT itself must perform the required transfer-function and gamut conversion. Does Core Image synthesize an Apple Log-aware input color space from kCVImageBufferLogTransferFunctionKey, or must applications implement Apple Log/Apple Log 2 decoding explicitly before using ordinary Core Image filters? Is there a supported CGColorSpace, ColorSync profile, or Core Image conversion API for this? Additionally, what numeric range does CIColorCube receive when its source is an x422 video-range Apple Log buffer— normalized Log RGB values, or values requiring explicit video-range conversion?
Replies
0
Boosts
0
Views
283
Activity
3d
Supported way to re-acquire genlock after follow() detaches mid-session?
Summary. iPhone 17 Pro Max + Blackmagic Camera ProDock, genlock BNC in from a generator confirmed at a true 30.00 fps. follow(_:videoFrameDuration:delegate:) reaches .activeSync in ~2 s. On one unit the lock then holds for 10+ minutes. On a second unit, same binary and same reference, it detaches 6 s to ~4 min after lock: .activeSync → .ready with input.externalSyncDevice == nil, no runtime error and no delegate error. Calling follow() again on the still-running session is rejected with -11800 every time, while unfollowExternalSyncDevice() plus a stopRunning()/startRunning() bounce recovers reliably. I am not looking for a fix. I want to know whether my call sequence is wrong, whether this transition is expected, and how a shipping app should be structured around it. Questions Is re-following a running session supported? Is the unfollow + bounce the intended reset sequence, or is there a lighter-weight way to clear whatever state the -11800 is keyed on? And once detached, is re-acquisition entirely the app's responsibility, or is the system expected to re-calibrate on its own while the reference is present? Is a hard detach a legal outcome for an already-calibrated input? The documentation describes .freeRunSync as the hold-over when a locked input loses sync. Is that hold-over guaranteed, or must an app also handle .activeSync → .ready with a nil externalSyncDevice? Is anything in my call sequence wrong (code in a reply below), and is polling input.externalSyncDevice the right signal to key recovery on, or is there a supported notification for detach? Setup. Video-only AVCaptureSession: one .builtInWideAngleCamera input, one AVCaptureVideoDataOutput. No multi-cam, no depth output, no synchronizer, no audio. Both frame durations set to CMTime(1, 30) before the input is created, never rewritten while a follow is live. Device A (iOS 26.0.1) holds: 601 s and 956 s runs, zero detaches. Device B (26.3.1, then 26.5.2) has 39 drops across 6 logs. The ProDock, cable and generator were swapped between units; the failure followed the phone. Caveat: n=2, and unit and OS build vary together. The detach. No AVCaptureSessionRuntimeError and no delegate error (-11892 has never been observed here, so this is not the documented frame-duration-mismatch path). The session keeps running and delivering frames. Status is .ready, not .unavailable — the ProDock stays enumerated, the reference unchanged. No confirmed drop has passed through .freeRunSync. PTS ground truth, independent of the follow state: while locked, every frame PTS sits exactly on the 1/30 grid, zero drift. At the drop there is exactly one teardown gap, 238–337 ms across 8 runs, after which the clock free-runs at ~30.013 fps (444 ppm) and never returns to the grid. On -11800. It surfaces through AVCaptureSessionRuntimeErrorNotification. The bounce recovers 3/3, with no activeFormat change. Caveat: -11800 is AVErrorUnknown and I see it in unrelated cases too, so I do not assume it is specific to retained follow state. Ruled out. Exposure duration — a run at ≤ 16.67 ms, within the recommendation in the follow() documentation, still drops. Reference drift — zero, by microsecond PTS. Accessory chain — full swap; the failure followed the phone. Another client — Final Cut Camera holds genlock on the fragile unit with the same ProDock and reference. Load and resolution — load changes time-to-drop, not whether it drops; with recording and audio off it still drops, and Device B drops at both 12 MP and 1080p. Code and supporting logs are in replies below; the length limit would not take them inline. Per-frame PTS CSVs, raw status logs, and a minimal Xcode project that still drops are available on request. Prior art read: forums/thread/799739 and thread/804594 — neither covers post-lock detach or re-acquisition.
Replies
3
Boosts
0
Views
713
Activity
4d
How to hide route button `showsRouteButton = false` in `MPVolumeView` without deprecation warning?
MPVolumeView's showsRouteButton was deprecated (https://developer.apple.com/documentation/mediaplayer/mpvolumeview/showsroutebutton?language=objc). It's not clear how can we now hide this button without deprecation warning. The documentation is lacking. Please advise. Thank you!
Replies
6
Boosts
0
Views
895
Activity
6d
Custom AVVideoCompositing on a composition-backed AVPlayerItem fails with AVErrorUnknown Xcode 27 beta 2 / beta 3
Trivial pass-through compositor fails on Xcode 27 (beta 2, beta 3); error code -11800 underlying error -12784. Repro included https://github.com/BugorBN/avplayer-custom-compositor-repro It works well on Xcode26 and lower
Replies
1
Boosts
2
Views
389
Activity
1w
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
569
Activity
1w
Programmatic / Background Trigger for ReplayKit Broadcast (Without User Intervention)
Hi everyone, I am working on an iOS application that utilizes a Broadcast Upload Extension (ReplayKit) to perform local, on-device screen analysis. Currently, we are using RPSystemBroadcastPickerView to allow the user to initiate the broadcast session. However, for our specific tracking use case, requiring the user to manually tap the "Start Broadcast" button every time creates a significant friction point in the user experience. My questions are: Is there any private API, entitlement, or MDM (Mobile Device Management) configuration that allows an app to programmatically start a ReplayKit screen recording session completely in the background without explicit human intervention (e.g., without tapping a button in the UI)? If this is strictly prohibited for consumer apps on the App Store due to privacy guidelines, are there any exceptions or enterprise-level profiles available for supervised devices that bypass the mandatory RPSystemBroadcastPickerView user interaction? My understanding is that Apple enforces this manual trigger and the red status bar indicator for strict security and privacy reasons, but I am looking for an official confirmation on whether any programmatic workaround exists for this in modern iOS versions (iOS 15+). Thank you in advance for your time and clarification!
Replies
1
Boosts
0
Views
265
Activity
1w
CarPlay: Is vehicle microphone capture without entering communications audio mode supported for third-party apps?
Hello, I am developing a native CarPlay application and would appreciate some clarification regarding AVAudioSession behaviour when using the vehicle microphone. The application is intentionally simple and designed to minimise driver distraction. The user presses a single button on the CarPlay screen, the application performs a brief (approximately five second) audio capture, performs application-specific processing on the captured audio, displays a simple confirmation to the user, and immediately releases the audio session. The goal is to allow the driver to continue their journey with as little interaction as possible. Because the audio being captured is often originating from the vehicle’s native DAB/FM radio rather than from the iPhone itself, preserving the existing listening experience during the brief capture is fundamental to the intended design. To better understand what is supported by CarPlay, I have carried out a number of controlled experiments. Test Environment Native CarPlay application Swift AVFoundation AVAudioSession AVAudioRecorder Vehicle connected via CarPlay Vehicle playing its native DAB radio Experiment 1 Configuration Category: AVAudioSession.Category.playAndRecord Mode: AVAudioSession.Mode.default Observed Route Input: CarPlay — CarAudio Output: CarPlay — CarAudio Result Recording succeeds using the vehicle microphone. Native DAB audio is muted during recording. The vehicle changes from “Audio Volume” to “Voice Volume”. When recording finishes and the AVAudioSession is deactivated, DAB resumes normally. Experiment 2 Changed only the session mode to: AVAudioSession.Mode.measurement Result Behaviour was identical to Experiment 1. Experiment 3 Changed only the session mode to: AVAudioSession.Mode.videoRecording Observed Route Input: iPhone microphone Output: CarPlay — CarAudio Result Input automatically switched from the vehicle microphone to the iPhone microphone. The vehicle remained in normal “Audio Volume”. However, the head unit switched away from its native DAB source to the CarPlay media source. Observation These experiments appear to suggest that the behaviour is specifically related to using the CarPlay “CarAudio” microphone route, rather than audio recording itself. Question Is this the expected behaviour for third-party CarPlay applications? More specifically: Is there any supported AVAudioSession configuration or CarPlay API that allows a third-party CarPlay application to perform a brief, user-initiated audio capture using the vehicle microphone without causing the head unit to enter its communications/voice audio mode or interrupt the vehicle’s native radio playback? If this behaviour is by design and no supported mechanism exists to achieve this, confirmation of that would be extremely valuable, as it would allow us to design the application accordingly. If additional information, sample code, AVAudioSession logs or detailed reproduction steps would be helpful, I would be more than happy to provide them. Thank you very much for your time. Kind regards, Neil Jenner Developer, HearSave
Replies
7
Boosts
0
Views
622
Activity
2w
ProResRAW shooting issue with AVCaptureMovieFileOutput with the first video
On iOS 26, the first ProRes RAW recording after launching the app consistently stalls for the entire take: audio records normally, but video contains only a few frames (for example, an 11.8-second clip at approximately 0.25 fps instead of 30 fps). Every subsequent recording works correctly. Before startRecording, the Bayer format is active, ProRes RAW is available, white balance is locked, and frame duration is pinned to 1/30. Reordering setOutputSettings and reasserting configuration have not resolved it; only discarding the first recording acts as a reliable warm-up. Is this a known one-time ProRes RAW encoder initialisation issue, and is there a supported way to prepare the encoder before the first user takes? I have a full working sample code if anyone needs but here is the setup function: import CoreMedia import CoreVideo @available(iOS 26.0, *) final class ProResRAWRecorder: NSObject, AVCaptureFileOutputRecordingDelegate { let session = AVCaptureSession() private let movieOutput = AVCaptureMovieFileOutput() private var camera: AVCaptureDevice! // Call on a serial capture queue. func configure() throws { session.beginConfiguration() defer { session.commitConfiguration() } session.sessionPreset = .inputPriority session.automaticallyConfiguresCaptureDeviceForWideColor = false guard let device = AVCaptureDevice.default( .builtInWideAngleCamera, for: .video, position: .back ) else { throw SampleError.noCamera } let cameraInput = try AVCaptureDeviceInput(device: device) guard session.canAddInput(cameraInput) else { throw SampleError.cannotAddInput } session.addInput(cameraInput) camera = device // Optional, but reproduces the real topology where audio remains healthy. if let microphone = AVCaptureDevice.default(for: .audio), let microphoneInput = try? AVCaptureDeviceInput(device: microphone), session.canAddInput(microphoneInput) { session.addInput(microphoneInput) } // Find a 12-bit packed Bayer format supporting 30 fps. guard let rawFormat = device.formats .filter({ CMFormatDescriptionGetMediaSubType($0.formatDescription) == kCVPixelFormatType_96VersatileBayerPacked12 && $0.videoSupportedFrameRateRanges.contains { $0.minFrameRate <= 30 && $0.maxFrameRate >= 30 } }) .max(by: { let a = CMVideoFormatDescriptionGetDimensions($0.formatDescription) let b = CMVideoFormatDescriptionGetDimensions($1.formatDescription) return Int(a.width) * Int(a.height) < Int(b.width) * Int(b.height) }) else { throw SampleError.noRAWFormat } try device.lockForConfiguration() device.activeFormat = rawFormat if rawFormat.supportedColorSpaces.contains(.appleLog) { device.activeColorSpace = .appleLog } let frameDuration = CMTime(value: 1, timescale: 30) device.activeVideoMinFrameDuration = frameDuration device.activeVideoMaxFrameDuration = frameDuration if device.isWhiteBalanceModeSupported(.locked) { device.whiteBalanceMode = .locked } device.unlockForConfiguration() guard session.canAddOutput(movieOutput) else { throw SampleError.cannotAddOutput } session.addOutput(movieOutput) session.startRunning() } // Call on the same serial capture queue after startRunning() returns. func record(to url: URL) throws { guard let connection = movieOutput.connection(with: .video) else { throw SampleError.noVideoConnection } // Reassert the required device state at the take boundary. try camera.lockForConfiguration() if camera.isWhiteBalanceModeSupported(.locked) { camera.whiteBalanceMode = .locked } let frameDuration = CMTime(value: 1, timescale: 30) camera.activeVideoMinFrameDuration = frameDuration camera.activeVideoMaxFrameDuration = frameDuration camera.unlockForConfiguration() guard movieOutput.availableVideoCodecTypes.contains(.proResRAW) else { throw SampleError.rawCodecUnavailable } movieOutput.setOutputSettings( [AVVideoCodecKey: AVVideoCodecType.proResRAW], for: connection ) movieOutput.startRecording(to: url, recordingDelegate: self) } func stop() { if movieOutput.isRecording { movieOutput.stopRecording() } } func fileOutput( _ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error? ) { print("Finished:", outputFileURL, "error:", error as Any) } enum SampleError: Error { case noCamera case cannotAddInput case noRAWFormat case cannotAddOutput case noVideoConnection case rawCodecUnavailable } }
Replies
1
Boosts
0
Views
1.1k
Activity
2w
Manual legible (subtitle) selection not honored on live LL-HLS — select(_:in:) reverts to “off” within ~2s; automatic selection never displays non-forced subtitles
Environment: iOS 18 / iOS 26, AVPlayer + AVPlayerItem, live low-latency HLS (LL-HLS). Subtitle renditions are regular (non-forced) WebVTT: AUTOSELECT=YES, FORCED=NO. System captioning (Closed Captions + SDH) is OFF (default). Direct CDN, no P2P. Master playlist (subtitle part): #EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Korean (AI)",AUTOSELECT=YES,FORCED=NO,LANGUAGE="kor",URI="..." #EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English (AI)",AUTOSELECT=YES,FORCED=NO,LANGUAGE="eng",URI="..." #EXT-X-STREAM-INF:...,SUBTITLES="subs" Problem When the user picks a subtitle language we call playerItem.select(option, in: legibleGroup) with an option that is a genuine member of the item's current legible group. Right after the call, item.currentMediaSelection.selectedMediaOption(in:) returns the requested option. But within ~2 seconds, on a subset of real-user sessions, the selection spontaneously reverts with no further app interaction: most often to Off (no legible output is delivered afterwards), or it stays stuck on the previously selected language (a subsequent select to another language, or to Off, is silently ignored). It is intermittent and only appears under real conditions (frequent live playlist reloads / reconnects); it does not reproduce in a short, clean session. What we verified The option passed to select(_:in:) is a real member of the current group (re-resolved from item.asset); the call returns without error and reads back correctly immediately after. appliesMediaSelectionCriteriaAutomatically is the default true. Per the AV Foundation Release Notes ("Advice about subtitles"), automatic selection excludes options that are not AVMediaCharacteristicContainsOnlyForcedSubtitles. So on each live reload the automatic re-selection resolves non-forced subtitles to Off, appearing to override the manual select(_:in:). Reading the selected option against a freshly obtained group instance returns the same value as against the original instance — so this is a real state change, not a mismatched-group read. Questions On live HLS reloads with appliesMediaSelectionCriteriaAutomatically == true, is manual select(_:in:) for .legible expected to be overridden by automatic media selection? If so, is setMediaSelectionCriteria(_:forMediaCharacteristic:) the intended way to persist a user's choice? setMediaSelectionCriteria for subtitles is itself reported as unreliable (sometimes no subtitles) — see thread 108403. What is the recommended, deterministic way to keep a user-selected non-forced subtitle displayed across live playlist reloads, including turning subtitles Off? Is this the same underlying behavior as FB13344652 ("Auto (Recommended) doesn't display subtitles despite language match / DEFAULT=YES")? Related: https://developer.apple.com/forums/thread/722752 (FB13344652) , https://developer.apple.com/forums/thread/108403
Replies
1
Boosts
0
Views
352
Activity
3w
iOS 27 beta: Opening Notification Center pauses AVPlayer playback
Since iOS 27 beta we are seeing a behavior change in our video streaming app and I would like to know whether others can reproduce it. Behavior on iOS 27 beta: Fully opening the Notification Center pauses playback. Audio continues for about 5 more seconds, then the app is suspended. Closing the Notification Center leaves the player paused. On iOS 26 the same build keeps playing in this situation. Setup: AVQueuePlayer playing video with audio, not muted audiovisualBackgroundPlaybackPolicy = .automatic (default) AVAudioSession category .playback, UIBackgroundModes: audio entitlement AVPictureInPictureController attached with canStartPictureInPictureAutomaticallyFromInline = true Filed as FB23893965 Questions: Can anyone reproduce this on iOS 27 beta? Is this intentional or a regression?
Replies
0
Boosts
2
Views
330
Activity
3w
Add a value to the Photos Caption field
In the iOS Photos app there is a caption field the user can write to. How can you write to this value from Swift when creating a photo? I see apps that do this, but there doesn't seem to be any official way to do this using the Photo library through PHAssetCreationRequest or PHAssetResourceCreationOptions or setting EXIF values, I tried settings a bunch of values there including IPTC values but nothing appears in the caption field in the iOS photos app. There must be some way to do it since I see other apps setting that value somehow after capturing a photo.
Replies
2
Boosts
1
Views
551
Activity
3w
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
477
Activity
3w
How should live latency be measured and maintained with AVPlayer (HLS / LL-HLS)?
We keep live playback at a consistent distance from the live edge using small playback rate adjustments, with a target range based on recommendedTimeOffsetFromLive. Since the live edge is not exposed by AVPlayer, we currently fall back to seekableTimeRanges.end as our best approximation. What should be treated as the live edge, and how should the current live latency be measured? Is rate adjustment the appropriate way to hold a target latency? While playing above 1.0x, the playhead can reach the seekable end, at which point AVPlayerItemDidPlayToEndTime fires and halts the live stream. How can we guard against ? Does any of this differ between regular HLS and LL-HLS? A clear statement of the intended contract here would resolve a lot of uncertainty. Thanks in advance.
Replies
2
Boosts
0
Views
463
Activity
4w
`LockedCameraCaptureManager` practically unusable since iOS 26
Somewhere since iOS 26, the LockedCameraCapture framework gets in an unpredictable state after opening the main app from the LockedCamera extension using LockedCameraCaptureSession.openApplication(for userActivity:). (Feedback with sample code to reproduce: FB21966835) Opening the extension from the lock screen again doesn’t open the extension but puts the lock screen in a state as if it has. Content updated from LockedCameraCaptureManager.shared.sessionContentUpdates comes in inconsistently, usually needs the app to be opened again or the extension to be opened. This makes using this extension impossible for me as I use it to record video files that manually need to be imported when the app is launched (so not through PhotoKit). Does anybody have a suggestion to circumvent this issue or how to get this fixed?
Replies
1
Boosts
2
Views
846
Activity
4w
iOS 26.4 regression: The `.pauses` audiovisual background playback policy does not pause video playback anymore when backgrounding the app
Starting with iOS 26.4 and the iOS 26.4 SDK, the .pauses audiovisual background playback policy is not correctly applied anymore to an AVPlayer having an attached video layer displayed on screen. This means that, when backgrounding a video-playing app (without Picture in Picture support) or locking the device, playback is not paused automatically by the system anymore. This issue affects the Apple TV application as well. We have filed FB22488151 with more information.
Replies
2
Boosts
0
Views
919
Activity
Jul ’26