Saltar al contenido

Primeros pasos con RealtimeKit

Esta guía lo guiará a través de la integración del complemento Capacitor RealtimeKit para agregar videoconferencias impulsadas por Cloudflare Calls a su aplicación.

Instale el complemento usando npm:

Terminal window
npm install @capgo/capacitor-realtimekit
npx cap sync

Este complemento utiliza Cloudflare RealtimeKit SDK:

  • iOS: RealtimeKitCoreiOS (instalado automáticamente a través del Administrador de paquetes Swift)
  • Android: com.cloudflare.realtimekit:ui-android versión 0.2.2

Personalizando Android Versión RealtimeKit

Section titled “Personalizando Android Versión RealtimeKit”

En build.gradle de tu aplicación:

buildscript {
ext {
realtimekitUiVersion = '0.2.2' // or your desired version
}
}

Agregue lo siguiente al Info.plist de su aplicación:

<key>NSCameraUsageDescription</key>
<string>We need camera access for video calls</string>
<key>NSMicrophoneUsageDescription</key>
<string>We need microphone access for audio calls</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need photo library access to share images</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>We need Bluetooth access for audio routing</string>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>voip</string>
<string>fetch</string>
<string>remote-notification</string>
</array>

Agregue los siguientes permisos a su AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
import { CapacitorRealtimekit } from '@capgo/capacitor-realtimekit';
async function initializeRealtimeKit() {
try {
await CapacitorRealtimekit.initialize();
console.log('RealtimeKit initialized');
} catch (error) {
console.error('Failed to initialize RealtimeKit:', error);
}
}
async function startMeeting(meetingUrl: string) {
try {
await CapacitorRealtimekit.startMeeting({
url: meetingUrl
});
console.log('Meeting started');
} catch (error) {
console.error('Failed to start meeting:', error);
}
}

Aquí tienes un servicio integral de videoconferencia:

import { CapacitorRealtimekit } from '@capgo/capacitor-realtimekit';
export interface MeetingConfig {
url: string;
displayName?: string;
audioEnabled?: boolean;
videoEnabled?: boolean;
}
export class VideoConferenceService {
private isInitialized = false;
private currentMeetingUrl: string | null = null;
async initialize(): Promise<boolean> {
if (this.isInitialized) {
console.log('RealtimeKit already initialized');
return true;
}
try {
await CapacitorRealtimekit.initialize();
this.isInitialized = true;
console.log('RealtimeKit initialized successfully');
return true;
} catch (error) {
console.error('Failed to initialize RealtimeKit:', error);
return false;
}
}
async startMeeting(config: MeetingConfig): Promise<void> {
if (!this.isInitialized) {
const initialized = await this.initialize();
if (!initialized) {
throw new Error('Failed to initialize RealtimeKit');
}
}
try {
await CapacitorRealtimekit.startMeeting({
url: config.url
});
this.currentMeetingUrl = config.url;
console.log('Meeting started:', config.url);
} catch (error) {
console.error('Failed to start meeting:', error);
throw error;
}
}
async joinMeeting(meetingUrl: string, displayName?: string): Promise<void> {
const config: MeetingConfig = {
url: meetingUrl,
displayName: displayName
};
await this.startMeeting(config);
}
getCurrentMeetingUrl(): string | null {
return this.currentMeetingUrl;
}
isInMeeting(): boolean {
return this.currentMeetingUrl !== null;
}
async getPluginVersion(): Promise<string> {
try {
const result = await CapacitorRealtimekit.getPluginVersion();
return result.version;
} catch (error) {
console.error('Failed to get plugin version:', error);
return 'unknown';
}
}
}
// Usage
const videoService = new VideoConferenceService();
// Initialize on app start
await videoService.initialize();
// Start a meeting
await videoService.startMeeting({
url: 'https://your-cloudflare-calls-url.com/meeting/123'
});
// Join an existing meeting
await videoService.joinMeeting(
'https://your-cloudflare-calls-url.com/meeting/456',
'John Doe'
);

Para utilizar este complemento, debe configurar Llamadas de Cloudflare:

Regístrate en Cloudflare si no tienes una cuenta.

  1. Navegue a su panel de Cloudflare
  2. Vaya a la sección Llamadas
  3. Habilite las llamadas API
  4. Obtenga sus credenciales API

Necesita un servicio de backend para crear URL de reuniones. Ejemplo de uso de trabajadores de Cloudflare:

// Cloudflare Worker example
export default {
async fetch(request: Request): Promise<Response> {
const { pathname } = new URL(request.url);
if (pathname === '/create-meeting') {
const meetingId = generateMeetingId();
const meetingUrl = `https://your-app.calls.cloudflare.com/${meetingId}`;
return new Response(JSON.stringify({
meetingId,
meetingUrl
}), {
headers: { 'Content-Type': 'application/json' }
});
}
return new Response('Not found', { status: 404 });
}
};
function generateMeetingId(): string {
return Math.random().toString(36).substring(2, 15);
}
class MeetingManager {
private videoService: VideoConferenceService;
constructor() {
this.videoService = new VideoConferenceService();
}
async createAndJoinMeeting(userName: string): Promise<string> {
// Initialize if needed
await this.videoService.initialize();
// Call your backend to create a meeting
const meetingUrl = await this.createMeetingOnBackend();
// Join the meeting
await this.videoService.joinMeeting(meetingUrl, userName);
return meetingUrl;
}
async createMeetingOnBackend(): Promise<string> {
const response = await fetch('https://your-api.com/create-meeting', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
const data = await response.json();
return data.meetingUrl;
}
async shareMeetingLink(meetingUrl: string) {
// Use Capacitor Share API
if ('share' in navigator) {
await (navigator as any).share({
title: 'Join my meeting',
text: 'Join me for a video call',
url: meetingUrl
});
}
}
}
import { Capacitor } from '@capacitor/core';
async function checkAndRequestPermissions(): Promise<boolean> {
if (Capacitor.getPlatform() === 'web') {
// Request browser permissions
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
// Stop the stream, we just needed permission
stream.getTracks().forEach(track => track.stop());
return true;
} catch (error) {
console.error('Permission denied:', error);
return false;
}
}
// On native platforms, permissions are requested automatically
return true;
}
// Use before starting a meeting
const hasPermissions = await checkAndRequestPermissions();
if (hasPermissions) {
await videoService.startMeeting({ url: meetingUrl });
}
import { useEffect, useState } from 'react';
import { VideoConferenceService } from './VideoConferenceService';
function VideoCallComponent() {
const [videoService] = useState(() => new VideoConferenceService());
const [isReady, setIsReady] = useState(false);
useEffect(() => {
videoService.initialize().then(setIsReady);
}, []);
const joinMeeting = async (meetingUrl: string) => {
if (!isReady) {
console.error('Video service not ready');
return;
}
try {
await videoService.joinMeeting(meetingUrl, 'User Name');
} catch (error) {
console.error('Failed to join meeting:', error);
}
};
return (
<div>
<button
onClick={() => joinMeeting('https://your-meeting-url')}
disabled={!isReady}
>
Join Meeting
</button>
</div>
);
}
import { ref, onMounted } from 'vue';
import { VideoConferenceService } from './VideoConferenceService';
export default {
setup() {
const videoService = new VideoConferenceService();
const isReady = ref(false);
onMounted(async () => {
isReady.value = await videoService.initialize();
});
const joinMeeting = async (meetingUrl: string) => {
if (!isReady.value) {
console.error('Video service not ready');
return;
}
try {
await videoService.joinMeeting(meetingUrl, 'User Name');
} catch (error) {
console.error('Failed to join meeting:', error);
}
};
return {
isReady,
joinMeeting
};
}
};
  1. Inicializar temprano: inicialice RealtimeKit cuando se inicie su aplicación
  2. Manejar errores: siempre envuelva las llamadas en bloques try-catch
  3. Solicitar permisos: asegúrese de tener permisos de cámara/micrófono antes de comenzar
  4. Probar red: verifique la conectividad a Internet antes de unirse
  5. Audio de fondo: configure los modos de fondo para iOS
  6. Experiencia de usuario: muestra los estados de carga durante la inicialización
  7. Limpiar URL: valide las URL de la reunión antes de usarlas
async function troubleshootMeeting(meetingUrl: string) {
// Check initialization
const version = await videoService.getPluginVersion();
console.log('Plugin version:', version);
// Verify URL format
if (!meetingUrl.startsWith('https://')) {
console.error('Invalid meeting URL, must use HTTPS');
return;
}
// Try starting meeting
try {
await videoService.startMeeting({ url: meetingUrl });
} catch (error) {
console.error('Meeting failed:', error);
}
}

El iOS, asegúrese de que Info.plist tenga todas las descripciones de uso requeridas. En Android, verifique que los permisos de AndroidManifest.xml estén presentes.

Asegúrese de que el modo de audio de fondo esté configurado en Info.plist:

<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>voip</string>
</array>