
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
Opus 1.6 audio encoding for React Native and Expo
Real-time audio capture and encoding using the latest Opus 1.6 codec, built from source with full native integration for iOS and Android.
Created as I had a need for real-time voice communication in a React Native app. Figured it could be useful to share with the community as it's a popular format for moving realtime audio over the internet!
Opus is the gold standard for real-time voice applications:
# Using npm
npm install opuslib
# Using yarn
yarn add opuslib
# Using pnpm
pnpm add opuslib
npx expo install opuslib
npx expo prebuild
# iOS
cd ios && pod install && cd ..
# Android - no additional steps needed
import Opuslib from 'opuslib';
import { Platform, PermissionsAndroid } from 'react-native';
// Request microphone permission (Android)
async function requestPermission() {
if (Platform.OS === 'android') {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.RECORD_AUDIO
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}
return true; // iOS handles permissions automatically
}
// Start recording and encoding
async function startRecording() {
// Request permission
const hasPermission = await requestPermission();
if (!hasPermission) {
console.error('Microphone permission denied');
return;
}
// Listen for encoded audio chunks
const subscription = Opuslib.addListener('audioChunk', (event) => {
const { data, timestamp, sequenceNumber } = event;
console.log(`Received ${data.byteLength} bytes of Opus audio`);
// Send to your backend, save to file, etc.
// data is an ArrayBuffer containing raw Opus packets (not packets you can write to an ogg)
});
// Start streaming
await Opuslib.startStreaming({
sampleRate: 16000, // 16 kHz
channels: 1, // Mono
bitrate: 24000, // 24 kbps
frameSize: 20, // 20ms frames
packetDuration: 100, // 100ms packets (5 frames)
});
console.log('Recording started!');
}
// Stop recording
async function stopRecording() {
await Opuslib.stopStreaming();
console.log('Recording stopped');
}
startStreaming(config: AudioConfig): Promise<void>Start audio capture and Opus encoding.
Parameters:
interface AudioConfig {
sampleRate: number; // Sample rate in Hz (8000, 16000, 24000, 48000)
channels: number; // Number of channels (1 = mono, 2 = stereo)
bitrate: number; // Target bitrate in bits/second (e.g., 24000)
frameSize: number; // Frame duration in ms (2.5, 5, 10, 20, 40, 60)
packetDuration: number; // Packet duration in ms (multiple of frameSize)
dredDuration?: number; // Reserved for future DRED support (default: 0)
enableAmplitudeEvents?: boolean; // Enable amplitude monitoring (default: false)
amplitudeEventInterval?: number; // Amplitude update interval in ms (default: 16)
}
Recommended Settings for Speech:
{
sampleRate: 16000, // 16 kHz - optimal for speech
channels: 1, // Mono - sufficient for voice
bitrate: 24000, // 24 kbps - excellent quality
frameSize: 20, // 20ms - standard for real-time
packetDuration: 100, // 100ms - good balance of latency/efficiency
}
Throws: Error if already streaming or if microphone permission denied
stopStreaming(): Promise<void>Stop audio capture and encoding, release resources.
pauseStreaming(): voidPause audio capture (keeps resources allocated). Call resumeStreaming() to continue.
resumeStreaming(): voidResume audio capture after calling pauseStreaming().
audioChunkEmitted when an encoded Opus packet is ready.
Opuslib.addListener('audioChunk', (event: AudioChunkEvent) => {
// event.data: ArrayBuffer - Raw Opus packet (ready to send/save)
// event.timestamp: number - Capture timestamp in milliseconds
// event.sequenceNumber: number - Packet sequence number (starts at 0)
});
Event Data:
interface AudioChunkEvent {
data: ArrayBuffer; // Raw Opus-encoded audio packet
timestamp: number; // Milliseconds since epoch
sequenceNumber: number; // Incrementing packet counter
}
amplitudeEmitted periodically with audio amplitude data (requires enableAmplitudeEvents: true).
Opuslib.addAmplitudeListener((event: AmplitudeEvent) => {
// event.rms: number - Root mean square amplitude (0.0 - 1.0)
// event.peak: number - Peak amplitude (0.0 - 1.0)
// event.timestamp: number - Milliseconds since epoch
});
Event Data:
interface AmplitudeEvent {
rms: number; // RMS amplitude (useful for average volume)
peak: number; // Peak amplitude (useful for clipping detection)
timestamp: number; // Milliseconds since epoch
}
errorEmitted when an error occurs during recording.
Opuslib.addErrorListener((event: ErrorEvent) => {
console.error(`Error: ${event.message}`);
});
Event Data:
interface ErrorEvent {
code: string; // Error code (e.g., "AUDIO_RECORD_ERROR")
message: string; // Human-readable error message
}
app.json:
{
"expo": {
"ios": {
"infoPlist": {
"NSMicrophoneUsageDescription": "This app needs microphone access to record audio."
}
}
}
}
import { PermissionsAndroid } from 'react-native';
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.RECORD_AUDIO
);
Benchmarks on iPhone 14 Pro and Pixel 7:
| Metric | iOS | Android |
|---|---|---|
| Encoding Latency | <2ms per 20ms frame | <3ms per 20ms frame |
| CPU Usage | ~2% (single core) | ~3% (single core) |
| Memory Usage | ~5MB | ~8MB |
| Battery Impact | Minimal | Minimal |
Note: Performance may vary based on device and configuration
Add NSMicrophoneUsageDescription to your Info.plist or app.json:
{
"expo": {
"ios": {
"infoPlist": {
"NSMicrophoneUsageDescription": "We need microphone access to record audio."
}
}
}
}
Request permission at runtime:
import { PermissionsAndroid, Platform } from 'react-native';
if (Platform.OS === 'android') {
await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.RECORD_AUDIO
);
}
Clean and reinstall pods:
cd ios
rm -rf Pods Podfile.lock opus-build
pod install
cd ..
Clean Gradle caches:
cd android
./gradlew clean
rm -rf .cxx build
cd ..
iOS:
Android:
The module compiles Opus 1.6 from source with the following CMake flags:
-DCMAKE_BUILD_TYPE=Release
-DOPUS_DRED=OFF # DRED disabled (future feature)
-DOPUS_BUILD_SHARED_LIBRARY=OFF # Static linking
-DOPUS_BUILD_TESTING=OFF # No tests
-DOPUS_BUILD_PROGRAMS=OFF # No CLI tools
iOS: Built as universal binary (arm64 + x86_64) for device and simulator Android: Built for arm64-v8a, armeabi-v7a, and x86_64
Contributions are welcome! Please read our Contributing Guidelines before submitting PRs.
# Clone the repository
git clone https://github.com/scdales/opuslib.git
cd opuslib
# Install dependencies
npm install
# Build TypeScript
npm run build
# Run example app
cd example
npm install
npx expo run:ios # or run:android
npm test
MIT License - see LICENSE file for details
opuslib@outlook.comSpecial thanks to the Opus development team for creating an exceptional codec, and the Expo team for their awesome module framework.
FAQs
Opuslib wrapper
We found that opuslib demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.