Migrate from Dolby.io to LiveKit.io

A comprehensive guide for developers looking to migrate their video conferencing applications

Open-Source Secure Scalable AI Agent Ready

Overview

Why Migrate?

LiveKit offers an open-source, self-hostable alternative to Dolby.io with competitive pricing, greater flexibility, and robust features. As a fully open-source solution, you gain complete control over your infrastructure.

Developer-Friendly

LiveKit provides comprehensive SDKs for web, iOS, Android, React, Flutter, and more with excellent documentation and a thriving community of developers.

Self-Hosting Option

Unlike Dolby.io, LiveKit can be self-hosted, giving you complete control over your infrastructure and data. Deploy on your own servers for maximum privacy and customization.

Secure & Scalable

LiveKit is built with security and scalability in mind. It uses WebRTC Selective Forwarding Unit (SFU) architecture to efficiently handle thousands of concurrent users while maintaining low latency.

AI Agent Ready

LiveKit's flexible architecture makes it perfect for integrating AI agents into your video applications. Build intelligent virtual assistants, real-time transcription, and more with ease.

Feature Comparison

Feature Dolby.io LiveKit.io
Pricing Model Usage-based pricing Usage-based pricing or self-hosted (free)
Self-Hosting Full control
Open Source
Video Quality Up to 720p Up to 4K with adaptive bitrate
Audio Processing Dolby Voice Standard audio processing
Recording
Screen Sharing
Chat
Custom Layouts
WebRTC Support Optimized SFU
AI Integration Limited Extensive support
Mobile SDKs iOS, Android iOS, Android, Flutter, React Native
Server APIs REST API REST API, gRPC
Scalability Limited Highly scalable architecture
Security Standard Enterprise-grade with customizable options

Migration Steps

1

Set Up LiveKit

Choose between LiveKit Cloud or self-hosting the LiveKit server.

# For self-hosting with Docker
docker run --rm -p 7880:7880 \
    -p 7881:7881 \
    -p 7882:7882/udp \
    -v $PWD/livekit.yaml:/livekit.yaml \
    livekit/livekit-server \
    --config /livekit.yaml
2

Install LiveKit SDKs

Replace Dolby.io SDKs with LiveKit equivalents.

# For web applications
npm install livekit-client

# For React applications
npm install @livekit/components-react
3

Update Authentication

Replace Dolby.io token generation with LiveKit token generation.

// Server-side token generation with Node.js
const { AccessToken } = require('livekit-server-sdk');

const createToken = (roomName, participantName) => {
  const at = new AccessToken('API_KEY', 'API_SECRET');
  at.addGrant({ roomJoin: true, room: roomName, canPublish: true, canSubscribe: true });
  at.identity = participantName;
  
  return at.toJwt();
};
4

Update Client Code

Replace Dolby.io client code with LiveKit equivalents.

// We'll see detailed code examples in the next section
5

Test and Deploy

Thoroughly test your application and deploy the updated version.

AI Integration Capabilities

LiveKit's flexible architecture makes it easy to integrate with various AI models and services, enabling powerful features like speech-to-text, text-to-speech, and intelligent virtual agents.

Speech-to-Text Integration

LiveKit can be easily integrated with various speech-to-text models and services:

  • OpenAI Whisper - High-accuracy multilingual transcription
  • Google Speech-to-Text - Real-time transcription with high accuracy
  • Azure Speech Service - Enterprise-grade speech recognition
  • Self-hosted models - Deploy your own models for complete control
// Example: Integrating OpenAI Whisper with LiveKit
import { Room, RoomEvent } from 'livekit-client';
import { AudioProcessor } from './audio-processor';
import { WhisperTranscriber } from './whisper-transcriber';

// Set up LiveKit room
const room = new Room();
await room.connect('wss://your-livekit-server.com', token);

// Initialize transcriber
const transcriber = new WhisperTranscriber({
  apiKey: 'your-openai-api-key',
  language: 'en'
});

// Process audio for transcription
room.localParticipant.on(RoomEvent.TrackPublished, async (publication) => {
  if (publication.kind === 'audio') {
    const audioTrack = publication.track;
    const processor = new AudioProcessor(audioTrack);
    
    // Process audio chunks and send to Whisper
    processor.onAudioChunk((audioChunk) => {
      transcriber.transcribe(audioChunk).then((text) => {
        console.log('Transcription:', text);
        // Use the transcription in your application
      });
    });
  }
});

Text-to-Speech Integration

Convert text to natural-sounding speech in your LiveKit applications:

  • Amazon Polly - Lifelike speech with many voices
  • Google Text-to-Speech - High-quality voice synthesis
  • ElevenLabs - Ultra-realistic voice generation
  • Local TTS models - Self-hosted options for privacy
// Example: Integrating ElevenLabs TTS with LiveKit
import { Room } from 'livekit-client';
import { ElevenLabsTTS } from './elevenlabs-tts';
import { AudioStreamer } from './audio-streamer';

// Set up LiveKit room
const room = new Room();
await room.connect('wss://your-livekit-server.com', token);

// Initialize TTS service
const tts = new ElevenLabsTTS({
  apiKey: 'your-elevenlabs-api-key',
  voiceId: 'voice-id-here',
  model: 'eleven_multilingual_v2'
});

// Function to speak text in the room
async function speakInRoom(text) {
  // Generate audio from text
  const audioBuffer = await tts.generateSpeech(text);
  
  // Create audio track from buffer and publish to room
  const audioStreamer = new AudioStreamer(audioBuffer);
  const audioTrack = audioStreamer.createTrack();
  
  // Publish the track to the room
  await room.localParticipant.publishTrack(audioTrack);
  
  // Start playing the audio
  audioStreamer.play();
}

AI Agents & Virtual Assistants

Create intelligent AI agents that can participate in your LiveKit sessions:

  • OpenAI Assistants API - Create context-aware agents
  • Anthropic Claude - Conversational AI with high reasoning
  • Custom agents - Build specialized agents for your use case
  • Multi-agent systems - Combine multiple agents for complex scenarios
// Example: Creating an AI agent participant in LiveKit
import { Room, LocalParticipant } from 'livekit-client';
import { OpenAIClient } from './openai-client';
import { AudioProcessor, TextToSpeech } from './audio-utils';

class AIAgentParticipant {
  constructor(room, options) {
    this.room = room;
    this.openai = new OpenAIClient(options.apiKey);
    this.tts = new TextToSpeech(options.ttsService);
    this.stt = new AudioProcessor();
    this.agentName = options.name || 'AI Assistant';
    this.setupEventListeners();
  }
  
  setupEventListeners() {
    // Listen for audio from participants
    this.room.on('trackSubscribed', (track, publication, participant) => {
      if (track.kind === 'audio') {
        // Process audio for speech recognition
        this.stt.processAudioTrack(track, async (text) => {
          if (text) {
            // Get AI response
            const response = await this.openai.getCompletion({
              messages: [{ role: 'user', content: text }],
              model: 'gpt-4'
            });
            
            // Convert response to speech and publish to room
            const audioTrack = await this.tts.createTrackFromText(response);
            await this.room.localParticipant.publishTrack(audioTrack);
          }
        });
      }
    });
  }
  
  // Join the room as an AI agent
  async join() {
    // Announce the AI agent has joined
    const welcomeMessage = `Hello, I'm ${this.agentName}, your AI assistant in this meeting.`;
    const audioTrack = await this.tts.createTrackFromText(welcomeMessage);
    await this.room.localParticipant.publishTrack(audioTrack);
  }
}

Code Examples

Dolby.io

// Initialize Dolby.io
VoxeetSDK.initialize('CONSUMER_KEY', 'CONSUMER_SECRET');

// Join a conference
const conferenceOptions = {
  alias: 'conference-name',
  params: {
    dolbyVoice: true
  }
};

VoxeetSDK.conference.create(conferenceOptions)
  .then((conference) => {
    const joinOptions = {
      constraints: {
        audio: true,
        video: true
      }
    };
    return VoxeetSDK.conference.join(conference, joinOptions);
  })
  .then(() => {
    console.log('Joined conference');
  })
  .catch((error) => {
    console.error(error);
  });

// Publish local stream
VoxeetSDK.conference.startVideo(VoxeetSDK.session.participant)
  .then(() => {
    console.log('Local video started');
  })
  .catch((error) => {
    console.error(error);
  });

// Handle participant joined
VoxeetSDK.conference.on('participantAdded', (participant) => {
  console.log(`${participant.info.name} joined`);
});

// Handle stream added
VoxeetSDK.conference.on('streamAdded', (participant, stream) => {
  if (participant.id !== VoxeetSDK.session.participant.id) {
    // Render remote participant's stream
    const videoNode = document.getElementById('remote-video');
    navigator.attachMediaStream(videoNode, stream);
  }
});

LiveKit.io

// Initialize LiveKit
import { Room, RoomEvent } from 'livekit-client';

const room = new Room();

// Connect to room with token from your server
async function connectToRoom(token) {
  await room.connect('wss://your-livekit-server.com', token);
  console.log('Connected to room:', room.name);

  // Publish local tracks
  const localTrackPublications = await room.localParticipant.enableCameraAndMicrophone();
  console.log('Published local tracks:', localTrackPublications);
}

// Handle participant joined
room.on(RoomEvent.ParticipantConnected, participant => {
  console.log(`${participant.identity} joined`);
});

// Handle track subscribed
room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
  if (track.kind === 'video') {
    // Attach video track to element
    const videoElement = document.createElement('video');
    videoElement.id = `video-${participant.identity}`;
    videoElement.autoplay = true;
    document.getElementById('remote-videos').appendChild(videoElement);
    track.attach(videoElement);
  }
});

// Connect with token from your server
connectToRoom('your-token-here')
  .catch(error => {
    console.error('Error connecting to room:', error);
  });

Dolby.io with React

// Using Dolby.io with React
import React, { useEffect } from 'react';
import { VoxeetSDK } from '@voxeet/voxeet-web-sdk';

function VideoConference() {
  useEffect(() => {
    // Initialize Dolby.io
    VoxeetSDK.initialize('CONSUMER_KEY', 'CONSUMER_SECRET');
    
    // Join conference
    const joinConference = async () => {
      try {
        const conferenceOptions = {
          alias: 'conference-name',
          params: { dolbyVoice: true }
        };
        
        const conference = await VoxeetSDK.conference.create(conferenceOptions);
        
        const joinOptions = {
          constraints: {
            audio: true,
            video: true
          }
        };
        
        await VoxeetSDK.conference.join(conference, joinOptions);
        console.log('Joined conference');
        
        // Start local video
        await VoxeetSDK.conference.startVideo(VoxeetSDK.session.participant);
      } catch (error) {
        console.error('Error joining conference:', error);
      }
    };
    
    joinConference();
    
    // Set up event listeners
    const onStreamAdded = (participant, stream) => {
      if (participant.id !== VoxeetSDK.session.participant.id) {
        const videoNode = document.getElementById('remote-video');
        navigator.attachMediaStream(videoNode, stream);
      }
    };
    
    VoxeetSDK.conference.on('streamAdded', onStreamAdded);
    
    // Cleanup
    return () => {
      VoxeetSDK.conference.off('streamAdded', onStreamAdded);
      VoxeetSDK.conference.leave();
    };
  }, []);
  
  return (
    
); } export default VideoConference;

LiveKit.io with React

// Using LiveKit with React
import React from 'react';
import {
  LiveKitRoom,
  VideoConference,
  GridLayout,
  ParticipantTile,
  RoomAudioRenderer,
  ControlBar
} from '@livekit/components-react';
import '@livekit/components-styles';

function VideoConferenceRoom() {
  // Token should be fetched from your server
  const token = 'your-token-here';
  const serverUrl = 'wss://your-livekit-server.com';
  
  return (
    
      {/* This provides a complete video conference UI */}
      
      
      {/* Or build your own UI with these components */}
      
        
      
      
      
    
  );
}

export default VideoConferenceRoom;

// For more custom control:
import React, { useEffect } from 'react';
import { useRoom, useParticipant } from '@livekit/components-react';

function CustomVideoConference() {
  const room = useRoom();
  
  useEffect(() => {
    // Custom room event handling
    const handleParticipantConnected = (participant) => {
      console.log(`${participant.identity} connected`);
    };
    
    room.on('participantConnected', handleParticipantConnected);
    
    return () => {
      room.off('participantConnected', handleParticipantConnected);
    };
  }, [room]);
  
  return (
    
{/* Your custom UI */}
); }

Dolby.io iOS

// Dolby.io iOS SDK (Swift)
import VoxeetSDK

class ConferenceViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Initialize SDK
        VoxeetSDK.shared.initialize(consumerKey: "CONSUMER_KEY", consumerSecret: "CONSUMER_SECRET")
        
        // Create a conference
        let conferenceOptions = VTConferenceOptions()
        conferenceOptions.alias = "conference-name"
        
        VoxeetSDK.shared.conference.create(options: conferenceOptions) { conference, error in
            guard let conference = conference, error == nil else {
                print("Error creating conference: \(error?.localizedDescription ?? "")")
                return
            }
            
            // Join the conference
            let joinOptions = VTJoinOptions()
            joinOptions.constraints.audio = true
            joinOptions.constraints.video = true
            
            VoxeetSDK.shared.conference.join(conference: conference, options: joinOptions) { error in
                if let error = error {
                    print("Error joining conference: \(error.localizedDescription)")
                    return
                }
                
                print("Joined conference successfully")
                
                // Start video
                VoxeetSDK.shared.conference.startVideo(participant: VoxeetSDK.shared.session.participant) { error in
                    if let error = error {
                        print("Error starting video: \(error.localizedDescription)")
                        return
                    }
                    
                    print("Local video started")
                }
            }
        }
    }
}

LiveKit.io iOS

// LiveKit iOS SDK (Swift)
import LiveKit

class ConferenceViewController: UIViewController {
    private var room: Room?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Create room
        room = Room()
        
        // Set up delegates
        room?.delegate = self
        
        // Connect to room with token from your server
        let url = "wss://your-livekit-server.com"
        let token = "your-token-here"
        
        Task {
            do {
                try await room?.connect(url: url, token: token)
                print("Connected to room: \(room?.name ?? "")")
                
                // Publish local tracks
                try await room?.localParticipant.setCamera(enabled: true)
                try await room?.localParticipant.setMicrophone(enabled: true)
                
                print("Published local tracks")
            } catch {
                print("Error connecting to room: \(error.localizedDescription)")
            }
        }
    }
}

// Room delegate
extension ConferenceViewController: RoomDelegate {
    func room(_ room: Room, didConnect isReconnect: Bool) {
        print("Room connected, reconnect: \(isReconnect)")
    }
    
    func room(_ room: Room, didDisconnect error: Error?) {
        if let error = error {
            print("Room disconnected with error: \(error.localizedDescription)")
        } else {
            print("Room disconnected")
        }
    }
    
    func room(_ room: Room, participant: RemoteParticipant, didSubscribe publication: RemoteTrackPublication, track: Track) {
        print("Subscribed to track: \(publication.trackSid) from participant: \(participant.identity)")
        
        if track.kind == .video {
            // Attach video track to view
            let videoView = VideoView()
            track.attach(videoView)
            
            // Add videoView to your UI
            // ...
        }
    }
}

Dolby.io Android

// Dolby.io Android SDK (Kotlin)
import com.voxeet.VoxeetSDK
import com.voxeet.android.media.MediaStream
import com.voxeet.android.media.MediaStreamType

class ConferenceActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_conference)
        
        // Initialize SDK
        VoxeetSDK.initialize("CONSUMER_KEY", "CONSUMER_SECRET")
        
        // Create and join conference
        val conferenceOptions = ConferenceCreateOptions.Builder()
            .setConferenceAlias("conference-name")
            .build()
        
        VoxeetSDK.conference().create(conferenceOptions)
            .then { conference ->
                val joinOptions = ConferenceJoinOptions.Builder(conference)
                    .setConstraints(MediaConstraints(true, true))
                    .build()
                
                VoxeetSDK.conference().join(joinOptions)
            }
            .then {
                // Start video
                VoxeetSDK.conference().startVideo()
                    .then {
                        Log.d("Conference", "Local video started")
                    }
                    .error { error ->
                        Log.e("Conference", "Error starting video: ${error.message}")
                    }
            }
            .error { error ->
                Log.e("Conference", "Error joining conference: ${error.message}")
            }
        
        // Set up listeners
        VoxeetSDK.conference().addListener(object : ConferenceListener {
            override fun onParticipantAdded(participant: Participant) {
                Log.d("Conference", "${participant.info.name} joined")
            }
            
            override fun onStreamAdded(participant: Participant, stream: MediaStream) {
                if (participant.id != VoxeetSDK.session().participant.id) {
                    runOnUiThread {
                        // Attach remote video
                        val videoView = findViewById(R.id.remote_video)
                        VoxeetSDK.conference().attachMediaStream(stream, videoView)
                    }
                }
            }
            
            // Other listener methods...
        })
    }
}

LiveKit.io Android

// LiveKit Android SDK (Kotlin)
import io.livekit.android.LiveKit
import io.livekit.android.room.Room
import io.livekit.android.room.track.Track
import io.livekit.android.room.track.VideoTrack

class ConferenceActivity : AppCompatActivity() {
    private lateinit var room: Room
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_conference)
        
        // Initialize LiveKit
        room = LiveKit.create(applicationContext)
        
        // Set up room listeners
        room.addEventListener(object : RoomListener {
            override fun onParticipantConnected(participant: Participant) {
                Log.d("LiveKit", "${participant.identity} connected")
            }
            
            override fun onTrackSubscribed(
                track: Track,
                publication: RemoteTrackPublication,
                participant: RemoteParticipant
            ) {
                if (track is VideoTrack) {
                    runOnUiThread {
                        // Attach video track to view
                        val videoView = findViewById(R.id.remote_video)
                        track.addRenderer(videoView)
                    }
                }
            }
            
            // Other listener methods...
        })
        
        // Connect to room with token from your server
        val url = "wss://your-livekit-server.com"
        val token = "your-token-here"
        
        lifecycleScope.launch {
            try {
                room.connect(url, token)
                Log.d("LiveKit", "Connected to room: ${room.name}")
                
                // Publish local tracks
                room.localParticipant.enableCameraAndMicrophone()
                Log.d("LiveKit", "Published local tracks")
            } catch (e: Exception) {
                Log.e("LiveKit", "Error connecting to room: ${e.message}")
            }
        }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        room.disconnect()
    }
}

Additional Resources

Ready to Migrate?

Start your migration journey from Dolby.io to LiveKit today and experience the benefits of an open-source, flexible video conferencing solution.

Begin Migration