Skip to main content

Overview

The createReactNativeWebRtcTransport function creates a WebRTC transport that connects to the OpenAI Realtime API using the react-native-webrtc library. It handles peer connection, data channels, audio streaming, and real-time event communication.

Usage

import { createReactNativeWebRtcTransport } from '@navai/voice-mobile';
import { mediaDevices, RTCPeerConnection } from 'react-native-webrtc';

const transport = createReactNativeWebRtcTransport({
  globals: {
    mediaDevices,
    RTCPeerConnection
  },
  model: 'gpt-4o-realtime-preview',
  remoteAudioTrackVolume: 10
});

// Connect to realtime API
await transport.connect({
  clientSecret: 'secret_abc123',
  onEvent: (event) => console.log('Realtime event:', event),
  onError: (error) => console.error('Transport error:', error)
});

// Send events
await transport.sendEvent({
  type: 'session.update',
  session: { instructions: 'You are a helpful assistant.' }
});

// Disconnect
await transport.disconnect();

Options

globals
NavaiReactNativeWebRtcGlobals
required
React Native WebRTC globals required for WebRTC functionality.
import { mediaDevices, RTCPeerConnection } from 'react-native-webrtc';

globals: {
  mediaDevices,
  RTCPeerConnection
}
model
string
Default model to use for realtime connections. Defaults to gpt-realtime.Can be overridden per connection in connect() options.
model: 'gpt-4o-realtime-preview'
realtimeUrl
string
OpenAI Realtime API endpoint URL. Defaults to https://api.openai.com/v1/realtime/calls.
realtimeUrl: 'https://api.openai.com/v1/realtime/calls'
remoteAudioTrackVolume
number
Volume multiplier for remote audio tracks (range: 0-10). Defaults to no adjustment.Note: Uses react-native-webrtc’s _setVolume API if available.
remoteAudioTrackVolume: 10 // Maximum volume
fetchImpl
typeof fetch
Custom fetch implementation. Defaults to global fetch.
fetchImpl: customFetch
rtcConfiguration
unknown
RTCConfiguration object for RTCPeerConnection. Use for custom ICE servers, etc.
rtcConfiguration: {
  iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
}
audioConstraints
unknown
MediaStreamConstraints for getUserMedia. Defaults to { audio: true, video: false }.
audioConstraints: {
  audio: {
    echoCancellation: true,
    noiseSuppression: true,
    autoGainControl: true
  },
  video: false
}

Return Value

Returns NavaiRealtimeTransport object:
connect
(options: NavaiRealtimeTransportConnectOptions) => Promise<void>
Connect to the OpenAI Realtime API.Throws error if connection fails.
disconnect
() => Promise<void>
Disconnect and clean up resources. Safe to call multiple times.
sendEvent
(event: unknown) => Promise<void>
Send a real-time event to the API.
await transport.sendEvent({
  type: 'session.update',
  session: { instructions: 'New instructions' }
});
Throws error if data channel is not open.
getState
() => NavaiRealtimeTransportState
Get current transport state.Returns: 'idle' | 'connecting' | 'connected' | 'error' | 'closed'

Examples

Basic Connection

import { createReactNativeWebRtcTransport } from '@navai/voice-mobile';
import { mediaDevices, RTCPeerConnection } from 'react-native-webrtc';

const transport = createReactNativeWebRtcTransport({
  globals: { mediaDevices, RTCPeerConnection }
});

await transport.connect({
  clientSecret: 'secret_abc123',
  model: 'gpt-4o-realtime-preview',
  onEvent: (event) => {
    console.log('Event:', event.type);
  },
  onError: (error) => {
    console.error('Error:', error);
  }
});

console.log('State:', transport.getState()); // 'connected'

With Voice Session

import {
  createNavaiMobileVoiceSession,
  createReactNativeWebRtcTransport,
  createNavaiMobileBackendClient
} from '@navai/voice-mobile';
import { mediaDevices, RTCPeerConnection } from 'react-native-webrtc';

const transport = createReactNativeWebRtcTransport({
  globals: { mediaDevices, RTCPeerConnection },
  model: 'gpt-4o-realtime-preview',
  remoteAudioTrackVolume: 10
});

const backendClient = createNavaiMobileBackendClient({
  apiBaseUrl: 'https://api.myapp.com'
});

const session = createNavaiMobileVoiceSession({
  transport,
  backendClient,
  onRealtimeEvent: (event) => {
    console.log('Realtime event:', event);
  },
  onRealtimeError: (error) => {
    console.error('Realtime error:', error);
  }
});

await session.start();

Custom Audio Constraints

const transport = createReactNativeWebRtcTransport({
  globals: { mediaDevices, RTCPeerConnection },
  audioConstraints: {
    audio: {
      echoCancellation: true,
      noiseSuppression: true,
      autoGainControl: true,
      sampleRate: 48000
    },
    video: false
  }
});

Custom RTC Configuration

const transport = createReactNativeWebRtcTransport({
  globals: { mediaDevices, RTCPeerConnection },
  rtcConfiguration: {
    iceServers: [
      { urls: 'stun:stun.l.google.com:19302' },
      { urls: 'stun:stun1.l.google.com:19302' }
    ],
    iceCandidatePoolSize: 10
  }
});

Sending Events

// Update session configuration
await transport.sendEvent({
  type: 'session.update',
  session: {
    type: 'realtime',
    instructions: 'You are a helpful fitness coach.',
    tools: [...],
    tool_choice: 'auto'
  }
});

// Create conversation item
await transport.sendEvent({
  type: 'conversation.item.create',
  item: {
    type: 'message',
    role: 'user',
    content: [{ type: 'text', text: 'Hello!' }]
  }
});

// Trigger response
await transport.sendEvent({
  type: 'response.create'
});

State Management

const transport = createReactNativeWebRtcTransport({ globals });

console.log(transport.getState()); // 'idle'

await transport.connect({ clientSecret: 'secret' });
console.log(transport.getState()); // 'connected'

await transport.disconnect();
console.log(transport.getState()); // 'closed'

Error Handling

const transport = createReactNativeWebRtcTransport({ globals });

try {
  await transport.connect({
    clientSecret: 'invalid_secret',
    onEvent: (event) => console.log(event),
    onError: (error) => {
      console.error('Transport error:', error);
    }
  });
} catch (error) {
  console.error('Connection failed:', error.message);
  console.log('State:', transport.getState()); // 'error'
}

Connection Lifecycle

  1. Idle: Initial state, no connection
  2. Connecting: WebRTC negotiation in progress
    • Create peer connection
    • Request microphone access
    • Create and configure data channel
    • Get local audio tracks
    • Create WebRTC offer
    • Send offer to OpenAI Realtime API
    • Receive answer and set remote description
    • Wait for data channel to open
  3. Connected: Ready to send/receive events
  4. Error: Connection failed or error occurred
  5. Closed: Disconnected and cleaned up

Remote Audio Volume

The remoteAudioTrackVolume option uses react-native-webrtc’s _setVolume API to adjust the volume of remote audio tracks:
const transport = createReactNativeWebRtcTransport({
  globals: { mediaDevices, RTCPeerConnection },
  remoteAudioTrackVolume: 10 // Maximum volume (range: 0-10)
});
Volume is applied:
  • When remote tracks are received (via ontrack event)
  • After remote description is set (via getReceivers)
Note: This feature requires react-native-webrtc’s private _setVolume API. If unavailable, volume adjustment is silently skipped.

Timeouts

  • Connection timeout: 12 seconds (CONNECT_DATA_CHANNEL_TIMEOUT_MS)
  • Send event timeout: 6 seconds (SEND_EVENT_DATA_CHANNEL_TIMEOUT_MS)
If the data channel doesn’t open within the timeout, an error is thrown.

Build docs developers (and LLMs) love