From 03b9ec8b751cd387c47521625d60d620ec5d32ff Mon Sep 17 00:00:00 2001 From: Can Bal Date: Thu, 24 Sep 2026 12:48:47 -0700 Subject: [PATCH 1/2] fix(ios): return frame-count duration from IOSFileWriter::closeFile closeFile() asked AVURLAsset for the duration of the file it had just closed. For containerless streams such as raw AAC (ADTS) AVFoundation has no duration to read and estimates size / early bitrate; with VBR AAC and a quiet opening that estimate runs 20-70% long. Return getCurrentDuration() (frames written / file sample rate) instead, read after the offloader has drained every queued buffer. This matches what the recorder already reports mid-recording and what Android returns at stop, and removes an AVFoundation asset open from the stop path. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../ios/audioapi/ios/core/utils/IOSFileWriter.mm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSFileWriter.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSFileWriter.mm index 6aa5149e9..0bd40734e 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSFileWriter.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSFileWriter.mm @@ -180,7 +180,8 @@ } /// @brief Closes the currently open audio file and finalizes writing. -/// This method retrieves the final file duration and size before closing. +/// The returned duration is the count of frames written rather than the AVURLAsset estimate, +/// which has no container duration to read for raw AAC (ADTS) and runs long for VBR. /// This method should be called from the JS thread only. /// @returns A CloseFileResult indicating success with file duration and size or an error message. CloseFileResult IOSFileWriter::closeFile() @@ -203,8 +204,8 @@ // AVAudioFile automatically finalizes the file when deallocated audioFile_ = nil; - double fileDuration = CMTimeGetSeconds([[AVURLAsset URLAssetWithURL:fileURL_ - options:nil] duration]); + // offloader_.reset() above drained every queued buffer, so the frame count is final. + double fileDuration = getCurrentDuration(); double fileSizeBytesMb = static_cast([[[NSFileManager defaultManager] attributesOfItemAtPath:fileURL_.path error:&error] fileSize]) / From 6441d0440b8ac65b7496c3afa04b62a579a1b905 Mon Sep 17 00:00:00 2001 From: michal Date: Fri, 25 Sep 2026 16:14:42 +0200 Subject: [PATCH 2/2] fix: cleanup comments --- .../ios/audioapi/ios/core/utils/IOSFileWriter.mm | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSFileWriter.mm b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSFileWriter.mm index 0bd40734e..b73ad4801 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSFileWriter.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/core/utils/IOSFileWriter.mm @@ -180,8 +180,6 @@ } /// @brief Closes the currently open audio file and finalizes writing. -/// The returned duration is the count of frames written rather than the AVURLAsset estimate, -/// which has no container duration to read for raw AAC (ADTS) and runs long for VBR. /// This method should be called from the JS thread only. /// @returns A CloseFileResult indicating success with file duration and size or an error message. CloseFileResult IOSFileWriter::closeFile() @@ -204,7 +202,6 @@ // AVAudioFile automatically finalizes the file when deallocated audioFile_ = nil; - // offloader_.reset() above drained every queued buffer, so the frame count is final. double fileDuration = getCurrentDuration(); double fileSizeBytesMb = static_cast([[[NSFileManager defaultManager] attributesOfItemAtPath:fileURL_.path