Video & Audio Rendering

Learn how to render video and audio streams in your application UI across all platforms

Overview

BaxCloud provides native video and audio rendering components across all platforms. The SDK handles participant management, track control, and provides easy-to-use rendering components for displaying video and audio in your application UI.

💡

Important

BaxCloud SDK provides built-in rendering components for all platforms. Simply use the participant objects with their state properties (cameraEnabled, microphoneEnabled, isSpeaking, etc.) and the platform-specific renderer components to display video and audio streams.

Video Tracks

Render camera and screen share video streams with hardware acceleration

Audio Tracks

Audio plays automatically, with controls for muting and volume

Multi-Participant

Render multiple participants in grid, gallery, or custom layouts

Video Rendering

Render video streams across all platforms

BaxCloud provides native video rendering components across all platforms. Each platform has optimized components for high-performance video display with built-in participant management.

React applications use BaxCloud's React components for rendering. These provide high-performance video rendering with built-in optimizations and hooks for participant management.

Basic Video Rendering

1import { BaxCloudVideoRenderer, BaxCloudAudioRenderer, useBaxCloudParticipants } from '@baxcloud/react-sdk';
2
3function VideoCall() {
4  const participants = useBaxCloudParticipants();
5  
6  return (
7    <div className="video-grid">
8      {participants.map(participant => (
9        <div key={participant.userId} className="video-tile">
10          {participant.cameraEnabled && (
11            <BaxCloudVideoRenderer 
12              userId={participant.userId} 
13              mirror={participant.isLocal}
14              style={{ width: '100%', height: '100%' }}
15            />
16          )}
17          {participant.microphoneEnabled && (
18            <BaxCloudAudioRenderer userId={participant.userId} />
19          )}
20          
21          {/* Participant info */}
22          <div className="participant-info">
23            <span>{participant.name} {participant.isLocal && '(You)'}</span>
24            {participant.isSpeaking && <span className="speaking-indicator">🎤</span>}
25          </div>
26        </div>
27      ))}
28    </div>
29  );
30}

Complete Multi-Participant Example

1import { useEffect, useState } from 'react';
2import { 
3  BaxCloudVideoRenderer, 
4  BaxCloudAudioRenderer, 
5  useBaxCloudParticipants,
6  BaxCloudClient 
7} from '@baxcloud/react-sdk';
8
9function VideoConference() {
10  const [client] = useState(() => new BaxCloudClient({
11    projectId: 'your-project-id',
12    apiKey: 'your-api-key',
13  }));
14  
15  const participants = useBaxCloudParticipants();
16  
17  useEffect(() => {
18    const connectToRoom = async () => {
19      await client.connect({
20        roomName: 'my-conference',
21        participant: {
22          userId: 'user-123',
23          name: 'John Doe',
24          isHost: true,
25        },
26        liveType: 'video_conference',
27      });
28      
29      // Enable camera and microphone
30      await client.enableCamera();
31      await client.enableMicrophone();
32    };
33    
34    connectToRoom();
35    
36    return () => {
37      client.disconnect();
38    };
39  }, [client]);
40  
41  const renderParticipant = (participant) => {
42    return (
43      <div 
44        key={participant.userId} 
45        className={`participant-tile ${participant.isLocal ? 'local' : 'remote'}`}
46        style={{
47          position: 'relative',
48          backgroundColor: '#1a1a1a',
49          borderRadius: '8px',
50          overflow: 'hidden',
51          aspectRatio: '16/9',
52        }}
53      >
54        {/* Video */}
55        {participant.cameraEnabled ? (
56          <BaxCloudVideoRenderer 
57            userId={participant.userId}
58            mirror={participant.isLocal}
59            style={{ 
60              width: '100%', 
61              height: '100%', 
62              objectFit: 'cover' 
63            }}
64          />
65        ) : (
66          // No video placeholder
67          <div style={{
68            position: 'absolute',
69            inset: 0,
70            display: 'flex',
71            alignItems: 'center',
72            justifyContent: 'center',
73            background: '#2a2a2a',
74          }}>
75            <div style={{
76              width: '80px',
77              height: '80px',
78              borderRadius: '50%',
79              background: '#3a3a3a',
80              display: 'flex',
81              alignItems: 'center',
82              justifyContent: 'center',
83              fontSize: '32px',
84              color: '#888',
85            }}>
86              {participant.name.charAt(0).toUpperCase()}
87            </div>
88          </div>
89        )}
90        
91        {/* Audio */}
92        {participant.microphoneEnabled && (
93          <BaxCloudAudioRenderer userId={participant.userId} />
94        )}
95        
96        {/* Participant info overlay */}
97        <div 
98          style={{
99            position: 'absolute',
100            bottom: 0,
101            left: 0,
102            right: 0,
103            padding: '8px',
104            background: 'linear-gradient(transparent, rgba(0,0,0,0.7))',
105          }}
106        >
107          <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
108            <span style={{ color: 'white', fontSize: '14px', fontWeight: 500 }}>
109              {participant.name}
110              {participant.isLocal && ' (You)'}
111            </span>
112            {!participant.microphoneEnabled && (
113              <span style={{ color: '#ef4444' }}>🔇</span>
114            )}
115            {participant.isSpeaking && (
116              <span style={{ color: '#10b981' }}>🎤</span>
117            )}
118          </div>
119        </div>
120      </div>
121    );
122  };
123  
124  return (
125    <div style={{ padding: '20px', height: '100vh', display: 'flex', flexDirection: 'column' }}>
126      <h1 style={{ marginBottom: '20px' }}>Video Conference</h1>
127      
128      <div style={{
129        display: 'grid',
130        gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))',
131        gap: '16px',
132        flex: 1,
133      }}>
134        {participants.map((participant) => renderParticipant(participant))}
135      </div>
136      
137      {/* Controls */}
138      <div style={{
139        marginTop: '20px',
140        display: 'flex',
141        gap: '12px',
142        justifyContent: 'center',
143      }}>
144        <button onClick={() => client.toggleCamera()}>
145          Toggle Camera
146        </button>
147        <button onClick={() => client.toggleMicrophone()}>
148          Toggle Mic
149        </button>
150        <button onClick={() => client.switchCamera()}>
151          Switch Camera
152        </button>
153        <button onClick={() => client.disconnect()}>
154          Leave
155        </button>
156      </div>
157    </div>
158  );
159}
The useBaxCloudParticipants() hook provides real-time participant data with state properties like cameraEnabled, microphoneEnabled, andisSpeaking. Use these to conditionally render video and show participant status.

Audio Rendering

Audio tracks play automatically in all platforms. However, you can control muting, volume levels, and audio output device selection.

Audio Controls

1// Mute/unmute local microphone
2await client.muteMicrophone();
3await client.unmuteMicrophone();
4
5// Toggle microphone
6await client.toggleMicrophone();
7
8// Audio plays automatically through BaxCloudAudioRenderer
9// Volume control handled by system audio settings
Audio tracks are rendered automatically by BaxCloud's audio components and the platform's audio system. Use the SDK's microphone control methods to manage audio state, and rely on participant properties like microphoneEnabled and isSpeaking for UI updates.

Best Practices

1. Always Clean Up Video Tracks on Unmount

Failing to properly clean up video renderers can cause memory leaks and prevent the camera from being released.

1useEffect(() => {
2  // Component mounted
3  return () => {
4    // Component unmounting - disconnect
5    client?.disconnect();
6  };
7}, [client]);

2. Use Participant State Properties

BaxCloud provides participant state properties for reactive UI updates.

1// React example with participant state
2const participants = useBaxCloudParticipants();
3
4{participants.map(participant => (
5  <div key={participant.userId}>
6    {participant.cameraEnabled && (
7      <BaxCloudVideoRenderer userId={participant.userId} />
8    )}
9    {!participant.microphoneEnabled && <MutedIcon />}
10    {participant.isSpeaking && <SpeakingIndicator />}
11  </div>
12))}

3. Optimize Video Display

Use appropriate object-fit values to prevent letterboxing and ensure video fills containers properly.

1// React: objectFit: 'cover'
2// React Native: use aspectRatio and flex
3// Flutter: fit: BoxFit.cover
4// iOS: mirror parameter on VideoView
5// Android: VideoView handles scaling automatically
6
7// This prevents letterboxing and ensures video fills the container

4. Use Aspect Ratio to Prevent Stretching

Maintain proper aspect ratios (16:9 for landscape, 9:16 for portrait) to prevent distortion.

1// CSS (React)
2.video-container {
3  aspect-ratio: 16 / 9;
4  width: 100%;
5}
6
7// React Native
8aspectRatio: 16 / 9
9
10// Flutter
11aspectRatio: 16 / 9

5. Handle Screen Rotation (Mobile)

On mobile devices, properly handle orientation changes to reflow your video grid.

1// React Native
2import { Dimensions } from 'react-native';
3
4const [orientation, setOrientation] = useState('portrait');
5
6useEffect(() => {
7  const subscription = Dimensions.addEventListener('change', ({ window }) => {
8    setOrientation(window.width > window.height ? 'landscape' : 'portrait');
9  });
10  return () => subscription?.remove();
11}, []);
12
13// Adjust grid columns based on orientation
14const columns = orientation === 'landscape' ? 3 : 2;