expo-aac-to-m4a
v1.0.5
Published
Expo Native Module to remux AAC files into M4A container without re-encoding (iOS only)
Maintainers
Readme
expo-aac-to-m4a
iOS-only Expo Native Module that remuxes an AAC/ADTS (
.aac) audio file into an M4A/MP4 (.m4a) container — without re-encoding. Preserves original codec, sample rate, channels, and bitrate.
Uses Apple's AVFoundation / AVAssetExportSession with AVAssetExportPresetPassthrough. No FFmpeg. No third-party dependencies.
Requirements
| Requirement | Version | |---|---| | Expo SDK | ≥ 49 | | iOS | ≥ 13.4 | | Xcode | ≥ 14 | | EAS Build / Expo Dev Client | Required (bare workflow) |
Android is not supported.
AVFoundationis Apple-only.
Installation
# npm
npm install expo-aac-to-m4a
# or yarn
yarn add expo-aac-to-m4aThen rebuild your native project:
# Expo Dev Client (local)
npx expo run:ios
# EAS Build (cloud)
eas build --platform ios --profile developmentNo manual iOS project changes required — expo-module.config.json handles auto-linking.
API
convertAacToM4a(inputPath, outputPath?)
convertAacToM4a(inputPath: string, outputPath?: string): Promise<string>| Parameter | Type | Description |
|---|---|---|
| inputPath | string | Absolute path to the source .aac file |
| outputPath | string (optional) | Absolute path for the output .m4a. Defaults to same directory/name as input with .m4a extension |
Returns: Promise<string> — absolute path of the created .m4a file.
Throws: Rejects with a descriptive error if the input file is invalid, unreadable, or export fails.
Usage Example
import { convertAacToM4a } from 'expo-aac-to-m4a';
import * as FileSystem from 'expo-file-system';
async function handleConvert() {
// Replace with the real path to your .aac file
const inputPath = FileSystem.documentDirectory + 'recording.aac';
try {
// Option A — auto-derive output path (same folder, .m4a extension)
const m4aPath = await convertAacToM4a(inputPath);
console.log('M4A saved at:', m4aPath);
// Option B — specify output path explicitly
const customPath = FileSystem.documentDirectory + 'converted/audio.m4a';
const m4aPath2 = await convertAacToM4a(inputPath, customPath);
console.log('M4A saved at:', m4aPath2);
} catch (error) {
console.error('Conversion failed:', error);
}
}How It Works
.aac (ADTS raw stream)
│
▼
AVURLAsset (load)
│
▼
AVAssetExportSession
preset = AVAssetExportPresetPassthrough ← no transcode
fileType = .m4a ← MP4 container
│
▼
.m4a (MP4 container, same AAC stream inside)The audio data is never decoded or re-encoded — only the container changes from a raw ADTS byte stream to an MPEG-4 (ISO Base Media) container. This is lossless and very fast.
Error Codes
| Code | Cause |
|---|---|
| ERR_DELETE_EXISTING | Could not remove a pre-existing file at outputPath |
| ERR_EXPORT_SESSION | AVAssetExportSession could not be initialized (bad input file) |
| ERR_EXPORT_FAILED | AVFoundation export failed (see error message for details) |
| ERR_EXPORT_CANCELLED | Export was cancelled externally |
| ERR_EXPORT_UNKNOWN | Unexpected export session status |
Project Structure
expo-aac-to-m4a/
├── src/
│ ├── index.ts # Public JS/TS API
│ └── ExpoAacToM4aModule.ts # Native module binding
├── ios/
│ ├── ExpoAacToM4aModule.swift # Swift implementation
│ └── ExpoAacToM4a.podspec # CocoaPods spec
├── expo-module.config.json # Expo auto-linking config
├── tsconfig.json
├── package.json
└── README.mdPublishing to npm
# 1. Build TypeScript
npm run build
# 2. Login to npm
npm login
# 3. Publish
npm publish --access publicLicense
MIT © Geet Purwar
