Local Participant·续1
const loadDevices = async () => { const audio = await meeting.self.getAudioDevices(); const video = await meeting.self.getVideoDevices(); setAudioDevices(audio); setVideoDevices(video); };
loadDevices(); }, [meeting]);
const handleDeviceChange = async (device) => { await meeting.self.setDevice(device); };
return ( <div> <select onChange={(e) => { const device = audioDevices.find( (d) => d.deviceId === e.target.value, ); handleDeviceChange(device); }} > {audioDevices.map((device) => ( <option key={device.deviceId} value={device.deviceId}> {device.label} </option> ))} </select> </div> ); }
Get current devices being used:
```jsx
const currentDevices = meeting.self.getCurrentDevices();
// Returns: { audio: MediaDeviceInfo, video: MediaDeviceInfo, speaker: MediaDeviceInfo }
// Get all audio devices
val audioDevices: List<AudioDevice> = meeting.localUser.getAudioDevices()
// Get all video devices
val videoDevices: List<VideoDevice> = meeting.localUser.getVideoDevices()
// Get currently selected audio device
val selectedAudioDevice: AudioDevice = meeting.localUser.getSelectedAudioDevice()
// Get currently selected video device
val selectedVideoDevice: VideoDevice = meeting.localUser.getSelectedVideoDevice()
// Get all audio devices
let audioDevices = meeting.localUser.getAudioDevices()
// Get all video devices
let videoDevices = meeting.localUser.getVideoDevices()
// Get currently selected audio device
let selectedAudioDevice = meeting.localUser.getSelectedAudioDevice()
// Get currently selected video device
let selectedVideoDevice = meeting.localUser.getSelectedVideoDevice()
import { useRealtimeKitClient } from "@cloudflare/realtimekit-react-native";
import { useState, useEffect } from "react";
import { FlatList, TouchableHighlight, Text, View } from "react-native";
function DeviceSelector() {
const [meeting] = useRealtimeKitClient();
const [audioDevices, setAudioDevices] = useState([]);
const [videoDevices, setVideoDevices] = useState([]);
useEffect(() => {
if (!meeting) return;
const loadDevices = async () => {
const audio = await meeting.self.getAudioDevices();
const video = await meeting.self.getVideoDevices();
setAudioDevices(audio);
setVideoDevices(video);
};
loadDevices();
}, [meeting]);
const handleDeviceChange = async (device) => {
await meeting.self.setDevice(device);
};
return (
<View>
<FlatList
data={audioDevices}
renderItem={({ item }) => (
<TouchableHighlight onPress={() => handleDeviceChange(item)}>
<Text>{item.label}</Text>
</TouchableHighlight>
)}
keyExtractor={(item) => item.deviceId}
/>
</View>
);
}
Get current devices being used:
const currentDevices = meeting.self.getCurrentDevices();
// Returns: { audio: MediaDeviceInfo, video: MediaDeviceInfo, speaker: MediaDeviceInfo }
Change device
Switch to a different media device:
// Get all devices
const devices = await meeting.self.getAllDevices();
// Set a specific device (replaces device of the same kind)
await meeting.self.setDevice(devices[0]);
Use the device selector example from the previous section. The handleDeviceChange function demonstrates how to switch devices.
// Get all audio devices
val audioDevices = meeting.localUser.getAudioDevices()
// Set audio device
meeting.localUser.setAudioDevice(audioDevices[0])
// Get all video devices
val videoDevices = meeting.localUser.getVideoDevices()
// Set video device
meeting.localUser.setVideoDevice(videoDevices[0])
// Switch between front and back camera on devices with 2 cameras
meeting.localUser.switchCamera()
// Set audio device
meeting.localUser.setAudioDevice(device)
// Set video device
meeting.localUser.setVideoDevice(videoDevice: device)
// Switch between front and back camera
meeting.localUser.switchCamera()
Use the device selector example from the previous section. The handleDeviceChange function demonstrates how to switch devices.
const handleDeviceChange = async (device) => {
await meeting.self.setDevice(device);
};
Display local video
Register video element
Attach the local video track to a <video> element:
<video id="local-video" autoplay playsinline></video>
const videoElement = document.getElementById("local-video");
// Register the video element to display video
meeting.self.registerVideoElement(videoElement);
// For local preview (not sent to other users), pass true as second argument
meeting.self.registerVideoElement(videoElement, true);
Deregister video element
Remove the video element when no longer needed:
meeting.self.deregisterVideoElement(videoElement);
Use UI Kit component
Display local video with the UI Kit video tile component:
import { RtkParticipantTile } from "@cloudflare/realtimekit-react-ui";
import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react";
function LocalVideo() {
const localUser = useRealtimeKitSelector((m) => m.self);
return <RtkParticipantTile participant={localUser} />;
}
Manage video element manually
Create custom video element implementations:
import {
useRealtimeKitClient,
useRealtimeKitSelector,
} from "@cloudflare/realtimekit-react";
import { useEffect, useRef } from "react";
function LocalVideoCustom() {
const [meeting] = useRealtimeKitClient();
const videoEnabled = useRealtimeKitSelector((m) => m.self.videoEnabled);
const videoTrack = useRealtimeKitSelector((m) => m.self.videoTrack);
const videoRef = useRef(null);
useEffect(() => {
if (!videoRef.current || !meeting) return;
// Register video element
meeting.self.registerVideoElement(videoRef.current);
return () => {
// Cleanup: deregister on unmount
meeting.self.deregisterVideoElement(videoRef.current);
};
}, [meeting]);
return (
<video
ref={videoRef}
autoPlay
playsInline
muted
style={{ display: videoEnabled ? "block" : "none" }}
/>
);
}
Get video view
Retrieve a self-preview video view that renders the local camera stream:
// Get the self-preview video view
val videoView = meeting.localUser.getSelfPreview()
For rendering other participants' video, use:
// Get video view for camera stream
val participantVideoView = participant.getVideoView()
// Get video view for screenshare stream
val screenshareView = participant.getScreenShareVideoView()
Manage lifecycle
Control video rendering with lifecycle methods:
// Start rendering video
videoView.renderVideo()
// Stop rendering video (but keep the view)
videoView.stopVideoRender()
// Release native resources when done
videoView.release()
Complete Example
import android.os.Bundle
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import io.dyte.core.VideoView
class MainActivity : AppCompatActivity() {
private lateinit var videoView: VideoView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Get the self-preview video view
videoView = meeting.localUser.getSelfPreview()
// Add to your layout
val container = findViewById<FrameLayout>(R.id.video_container)
container.addView(videoView)
// Start rendering
videoView.renderVideo()
}
override fun onPause() {
super.onPause()
// Stop rendering when activity is paused
videoView.stopVideoRender()
}
override fun onResume() {
super.onResume()
// Resume rendering when activity is resumed
videoView.renderVideo()
}
override fun onDestroy() {
super.onDestroy()
// Clean up resources
videoView.release()
}
}
Get video view
Retrieve video views that render the participant's video streams:
// Get video view for local camera stream
let videoView = meeting.localUser.getVideoView()
// Get video view for screenshare stream
let screenshareView = meeting.localUser.getScreenShareVideoView()
Manage lifecycle
The UIView handles its own lifecycle automatically and cleans up native resources when it exits the current window. No manual cleanup is required.
Example
import UIKit
import RealtimeKit
class VideoViewController: UIViewController {
private var videoView: UIView?
override func viewDidLoad() {
super.viewDidLoad()
// Get the video view for local camera
videoView = meeting.localUser.getVideoView()
// Add to your view hierarchy
if let videoView = videoView {
videoView.frame = view.bounds
videoView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(videoView)
}
}
}
For screenshare:
// Get and display screenshare view
let screenshareView = meeting.localUser.getScreenShareVideoView()
if let screenshareView = screenshareView {
screenshareView.frame = view.bounds
screenshareView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(screenshareView)
}
import React from "react";
import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react-native";
import { MediaStream, RTCView } from "@cloudflare/react-native-webrtc";
export default function VideoView() {
const { videoTrack } = useRealtimeKitSelector(
(m) => m.participants.active,
).toArray()[0];
const stream = new MediaStream(undefined);
stream.addTrack(videoTrack);
return (
<RTCView
objectFit={"cover"}
style={{ flex: 1 }}
streamURL={stream.toURL()}
mirror={true}
zOrder={1}
/>
);
}
Events
Room joined
Fires when the local user joins the meeting:
meeting.self.on("roomJoined", () => {
console.log("Successfully joined the meeting");
});
const roomJoined = useRealtimeKitSelector((m) => m.self.roomJoined);
useEffect(() => {
if (roomJoined) {
console.log("Successfully joined the meeting");
}
}, [roomJoined]);
Or use event listener:
useEffect(() => {
if (!meeting) return;
const handleRoomJoined = () => {
console.log("Successfully joined the meeting");
};
meeting.self.on("roomJoined", handleRoomJoined);
return () => {
meeting.self.off("roomJoined", handleRoomJoined);
};
}, [meeting]);
Android SDK uses a different event model. Monitor roomJoined property changes or use listeners for state changes.
iOS SDK uses a different event model. Monitor roomJoined property changes or use listeners for state changes.
const roomJoined = useRealtimeKitSelector((m) => m.self.roomJoined);
useEffect(() => {
if (roomJoined) {
console.log("Successfully joined the meeting");
}
}, [roomJoined]);
Or use event listener:
useEffect(() => {
if (!meeting) return;
const handleRoomJoined = () => {
console.log("Successfully joined the meeting");
};
meeting.self.on("roomJoined", handleRoomJoined);
return () => {
meeting.self.off("roomJoined", handleRoomJoined);
};
}, [meeting]);
Room left
Fires when the local user leaves the meeting:
meeting.self.on("roomLeft", ({ state }) => {
console.log("Left the meeting with state:", state);
// Handle different leave states
if (state === "left") {
console.log("User voluntarily left");
} else if (state === "kicked") {
console.log("User was kicked from the meeting");
} else if (state === "ended") {
console.log("Meeting has ended");
} else if (state === "disconnected") {
console.log("Lost connection to meeting");
}
});
Possible state values: 'left', 'kicked', 'ended', 'rejected', 'disconnected', 'failed'
const roomJoined = useRealtimeKitSelector((m) => m.self.roomJoined);
useEffect(() => {
if (!roomJoined) {
console.log("Left the meeting");
}
}, [roomJoined]);
Or use event listener for detailed state:
meeting.self.on("roomLeft", ({ state }) => {
if (state === "left") {
console.log("User voluntarily left");
} else if (state === "kicked") {
console.log("User was kicked");
}
});
Use RtkSelfEventListener to monitor when the local user is removed from the meeting:
meeting.addSelfEventListener(object : RtkSelfEventListener {
override fun onRemovedFromMeeting() {
// display alert that user is no longer in the meeting
}
})
iOS SDK uses a different event model. Monitor roomJoined property changes or use listeners for state changes.
const roomJoined = useRealtimeKitSelector((m) => m.self.roomJoined);
useEffect(() => {
if (!roomJoined) {
console.log("Left the meeting");
}
}, [roomJoined]);
Or use event listener for detailed state:
meeting.self.on("roomLeft", ({ state }) => {
if (state === "left") {
console.log("User voluntarily left");
} else if (state === "kicked") {
console.log("User was kicked");
}
});
Video update
Fires when video is enabled or disabled:
meeting.self.on("videoUpdate", ({ videoEnabled, videoTrack }) => {
console.log("Video state:", videoEnabled);
if (videoEnabled) {
// Video track is available, can display it
const videoElement = document.getElementById("my-video");
const stream = new MediaStream();
stream.addTrack(videoTrack);
videoElement.srcObject = stream;
videoElement.play();
}
});
const videoEnabled = useRealtimeKitSelector((m) => m.self.videoEnabled);
const videoTrack = useRealtimeKitSelector((m) => m.self.videoTrack);
useEffect(() => {
if (videoEnabled && videoTrack) {
console.log("Video is enabled");
// Handle video track
}
}, [videoEnabled, videoTrack]);
meeting.addSelfEventListener(object : RtkSelfEventListener {
override fun onVideoUpdate(isEnabled: Boolean) {
if (isEnabled) {
// video is enabled, other participants can see local user
} else {
// video is disabled, other participants cannot see local user
}
}
})
extension MeetingViewModel: RtkSelfEventListener {
func onVideoUpdate(isEnabled: Bool) {
if (isEnabled) {
// video is enabled, other participants can see local user
} else {
// video is disabled, other participants cannot see local user
}
}
}
const videoEnabled = useRealtimeKitSelector((m) => m.self.videoEnabled);
const videoTrack = useRealtimeKitSelector((m) => m.self.videoTrack);
useEffect(() => {
if (videoEnabled && videoTrack) {
console.log("Video is enabled");
// Handle video track
}
}, [videoEnabled, videoTrack]);
Audio update
Fires when audio is enabled or disabled:
meeting.self.on("audioUpdate", ({ audioEnabled, audioTrack }) => {
console.log("Audio state:", audioEnabled);
if (audioEnabled) {
// Audio track is available
console.log("Microphone is on");
}
});
const audioEnabled = useRealtimeKitSelector((m) => m.self.audioEnabled);
const audioTrack = useRealtimeKitSelector((m) => m.self.audioTrack);
useEffect(() => {
if (audioEnabled && audioTrack) {
console.log("Audio is enabled");
// Handle audio track
}
}, [audioEnabled, audioTrack]);
meeting.addSelfEventListener(object : RtkSelfEventListener {
override fun onAudioUpdate(isEnabled: Boolean) {
if (isEnabled) {
// audio is enabled, other participants can hear local user
} else {
// audio is disabled, other participants cannot hear local user
}
}
})
extension MeetingViewModel: RtkSelfEventListener {
func onAudioUpdate(isEnabled: Bool) {
if (isEnabled) {
// audio is enabled, other participants can hear local user
} else {
// audio is disabled, other participants cannot hear local user
}
}
}
const audioEnabled = useRealtimeKitSelector((m) => m.self.audioEnabled);
const audioTrack = useRealtimeKitSelector((m) => m.self.audioTrack);
useEffect(() => {
if (audioEnabled && audioTrack) {
console.log("Audio is enabled");
// Handle audio track
}
}, [audioEnabled, audioTrack]);
Screen share update
Fires when screen sharing starts or stops:
meeting.self.on(
"screenShareUpdate",
({ screenShareEnabled, screenShareTracks }) => {
console.log("Screen share state:", screenShareEnabled);
if (screenShareEnabled) {
// Screen share tracks are available
const screenElement = document.getElementById("my-screen-share");
const stream = new MediaStream();
stream.addTrack(screenShareTracks.video);
if (screenShareTracks.audio) {
stream.addTrack(screenShareTracks.audio);
}
screenElement.srcObject = stream;
screenElement.play();
}
},
);
const screenShareEnabled = useRealtimeKitSelector(
(m) => m.self.screenShareEnabled,
);
const screenShareTracks = useRealtimeKitSelector(
(m) => m.self.screenShareTracks,
);
useEffect(() => {
if (screenShareEnabled && screenShareTracks) {
console.log("Screen sharing is active");
// Handle screen share tracks
}
}, [screenShareEnabled, screenShareTracks]);
