From b1c80ebcd2f419b6cf41ddcea6f38b44c1142d38 Mon Sep 17 00:00:00 2001 From: Sean Holbert Date: Wed, 23 Sep 2026 11:34:58 -0700 Subject: [PATCH] feat: add ADTS AAC file format for crash-recoverable compressed recording Adds FileFormat.Adts as a recorder output option. ADTS frames are self-delimiting (each carries a sync word and a frame-length header), so a recording interrupted by a crash or force-kill stays decodable up to the last complete frame - the same recoverability FLAC offers today, at lossy-AAC file sizes (~10x smaller) and with no post-processing transcode. iOS: maps ADTS to kAudioFormatMPEG4AAC with an .aac extension; the existing AAC bitrate settings path applies unchanged. Android: the FFmpeg writer produces ADTS without a muxer. The prebuilt FFmpeg from software-mansion-labs/rn-audio-libs is configured with --enable-muxer=wav,mp4,flac,caf (no adts), so avformat_alloc_output_context2("adts") would fail at record start. ADTS does not need one: for each encoded AAC packet the writer builds the 7-byte ADTS header by hand (syncword 0xFFF, MPEG-4, AAC-LC profile, sample-rate index from the encoder rate, channel configuration, 13-bit frame length = 7 + packet size, buffer fullness 0x7FF) and writes header plus raw packet straight to an AVIOContext opened with avio_open. The encoder, resampler and FIFO paths are unchanged; the periodic avio_flush is retained on the ADTS AVIOContext so crash durability is the same as the container path. No trailer is written - an ADTS stream is complete after its last frame. rollbackFailedOpen() closes the ADTS AVIOContext and clears the flag, and finalizeOutput() derives the ADTS duration from nextPts_ / encoder sample rate like the container path. Co-Authored-By: Claude Fable 5.1 --- .../audiodocs/docs/inputs/audio-recorder.mdx | 5 +- .../audiodocs/docs/other/runtime-flags.mdx | 2 +- .../android/core/utils/FileOptions.cpp | 2 + .../utils/ffmpegBackend/FFmpegFileWriter.cpp | 164 ++++++++++++++++-- .../utils/ffmpegBackend/FFmpegFileWriter.h | 10 ++ .../core/utils/ffmpegBackend/utils.cpp | 4 +- .../cpp/audioapi/utils/AudioFileProperties.h | 1 + .../audioapi/ios/core/utils/FileOptions.mm | 6 + packages/react-native-audio-api/src/types.ts | 1 + 9 files changed, 174 insertions(+), 21 deletions(-) diff --git a/packages/audiodocs/docs/inputs/audio-recorder.mdx b/packages/audiodocs/docs/inputs/audio-recorder.mdx index 2ae695f89..67688253b 100644 --- a/packages/audiodocs/docs/inputs/audio-recorder.mdx +++ b/packages/audiodocs/docs/inputs/audio-recorder.mdx @@ -947,11 +947,14 @@ enum FileFormat { Caf, M4A, Flac, + Adts, } ``` +`Adts` writes raw AAC-LC in ADTS framing (`.aac` extension). Every frame carries its own sync word and length header, so a recording interrupted by a crash or force-kill stays decodable up to the last complete frame, like FLAC but at lossy-AAC file sizes. Bitrate and quality follow the same AAC properties as `M4A`. + :::caution Android + FFmpeg -On Android, encoded file output for `M4A`, `FLAC`, and `CAF` uses FFmpeg. When FFmpeg is disabled in the build, only **WAV** recording to file is supported. iOS uses system AVFoundation for all listed formats. See [Runtime flags](../other/runtime-flags.mdx#where-ffmpeg-is-used). +On Android, encoded file output for `M4A`, `FLAC`, `CAF`, and `ADTS` uses FFmpeg. When FFmpeg is disabled in the build, only **WAV** recording to file is supported. iOS uses system AVFoundation for all listed formats. See [Runtime flags](../other/runtime-flags.mdx#where-ffmpeg-is-used). ::: #### FileInfo diff --git a/packages/audiodocs/docs/other/runtime-flags.mdx b/packages/audiodocs/docs/other/runtime-flags.mdx index c65709891..2d3ab9724 100644 --- a/packages/audiodocs/docs/other/runtime-flags.mdx +++ b/packages/audiodocs/docs/other/runtime-flags.mdx @@ -31,7 +31,7 @@ if (!isFfmpegEnabled()) { | :---: | :---: | :---- | | Streaming | [`Audio tag`](../sources/audio-tag.mdx), `createFileSource` | Remote URL streaming (HTTP byte ranges) and HLS (`.m3u8`) | | Metadata | [`getAudioDuration`](../utils/decoding.mdx#getaudioduration), [`