Live Room Viewing
This guide covers how to view and play live rooms in real-time, including room discovery, player setup, and handling different room states.Room Discovery & Loading
Retrieve Room from Post
Get a room from a post to access room metadata and playback information:Copy
Ask AI
import AmitySDK
import AVKit
class LiveRoomViewer {
func getRoomFromPost(_ post: AmityPost) {
guard let roomData = post.getRoomInfo() else {
print("No room data available")
return
}
// Access room information
print("Room ID: \(roomData.roomId)")
print("Status: \(roomData.status)")
// Handle different room states
switch roomData.status {
case .live:
// Room is currently live - ready to play
if let liveUrl = roomData.getLivePlaybackUrl() {
playLiveRoom(url: liveUrl)
}
case .recorded:
// Room has ended and recording is available
let recordedInfos = roomData.getRecordedPlaybackInfos()
if let firstUrl = recordedInfos.first?.url {
playRecordedRoom(url: firstUrl)
}
case .ended:
// Room ended but recording may not be ready
showMessage("Room has ended, recording will be available soon")
case .idle:
// Room created but not started
showMessage("Room hasn't started yet")
default:
break
}
}
private func playLiveRoom(url: String) {
guard let videoURL = URL(string: url) else { return }
let player = AVPlayer(url: videoURL)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
present(playerViewController, animated: true) {
player.play()
}
}
}
Room States & Status
Understanding room lifecycle and handling state transitions:Room Status Types
| Status | Description | Playback Action |
|---|---|---|
| idle | Room created but not started | Show “Coming Soon” message |
| live | Currently broadcasting | Use getLivePlaybackUrl() |
| ended | Broadcasting finished, processing recording | Show “Processing” message |
| recorded | Recording available for playback | Use getRecordedPlaybackInfos() |
Real-Time State Monitoring
Copy
Ask AI
class RoomStateManager {
private var roomToken: AmityNotificationToken?
func observeRoomState(roomId: String) {
let roomRepository = AmityVideoClient.newRoomRepository()
let liveRoom = roomRepository.getRoom(roomId)
roomToken = liveRoom.observe { [weak self] liveObject, error in
guard let room = liveObject.object else { return }
self?.updateUI(for: room)
}
}
private func updateUI(for room: AmityRoom) {
DispatchQueue.main.async {
switch room.status {
case .idle:
self.hidePlayer()
self.showWaitingMessage("Room will start soon...")
case .live:
self.showPlayer()
if let liveUrl = room.getLivePlaybackUrl() {
self.startLivePlayback(url: liveUrl)
}
self.showLiveIndicator(true)
case .ended:
self.stopPlayback()
self.showMessage("Room has ended. Recording will be available soon.")
case .recorded:
self.showPlayer()
let recordedInfos = room.getRecordedPlaybackInfos()
if let firstUrl = recordedInfos.first?.url {
self.startRecordedPlayback(url: firstUrl)
}
self.showLiveIndicator(false)
default:
break
}
}
}
}
Live Room Playback
Getting Live Playback URL
UsegetLivePlaybackUrl() to get the URL for live room playback:
Copy
Ask AI
func getLivePlaybackUrl(from room: AmityRoom) -> String? {
guard room.status == .live else {
print("Room is not live")
return nil
}
return room.getLivePlaybackUrl()
}
Platform-Specific Player Setup
iOS - AVPlayer for Live Rooms
Copy
Ask AI
import AVKit
class LiveRoomPlayer {
private var player: AVPlayer?
private var playerViewController: AVPlayerViewController?
func setupPlayer() -> AVPlayerViewController {
playerViewController = AVPlayerViewController()
return playerViewController!
}
func playLiveRoom(url: String) {
guard let videoURL = URL(string: url) else { return }
player = AVPlayer(url: videoURL)
playerViewController?.player = player
// Start playback
player?.play()
}
func pause() {
player?.pause()
}
func resume() {
player?.play()
}
func cleanup() {
player?.pause()
player = nil
playerViewController = nil
}
}
Android - ExoPlayer for Live Rooms
Copy
Ask AI
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
class LiveRoomPlayer(private val context: Context) {
private var exoPlayer: ExoPlayer? = null
fun setupPlayer(playerView: PlayerView) {
exoPlayer = ExoPlayer.Builder(context).build()
playerView.player = exoPlayer
// Add player event listeners
exoPlayer?.addListener(object : Player.Listener {
override fun onPlaybackStateChanged(state: Int) {
when (state) {
Player.STATE_READY -> Log.d("Player", "Ready to play")
Player.STATE_BUFFERING -> Log.d("Player", "Buffering...")
Player.STATE_ENDED -> Log.d("Player", "Playback ended")
Player.STATE_IDLE -> Log.d("Player", "Player idle")
}
}
override fun onPlayerError(error: PlaybackException) {
Log.e("Player", "Playback error: ${error.message}")
}
})
}
fun playLiveRoom(url: String) {
val mediaItem = MediaItem.fromUri(url)
exoPlayer?.setMediaItem(mediaItem)
exoPlayer?.prepare()
exoPlayer?.playWhenReady = true
}
fun pause() {
exoPlayer?.pause()
}
fun resume() {
exoPlayer?.play()
}
fun release() {
exoPlayer?.release()
exoPlayer = null
}
}
TypeScript - HTML5 Video Player
Copy
Ask AI
class LiveRoomPlayer {
private videoElement: HTMLVideoElement;
private hls: Hls | null = null;
constructor(videoElementId: string) {
this.videoElement = document.getElementById(videoElementId) as HTMLVideoElement;
this.setupEventListeners();
}
async playLiveRoom(url: string) {
try {
if (url.includes('.m3u8')) {
await this.setupHLSPlayer(url);
} else {
this.videoElement.src = url;
await this.videoElement.play();
}
} catch (error) {
console.error('Failed to play live room:', error);
throw error;
}
}
private async setupHLSPlayer(hlsUrl: string) {
if (Hls.isSupported()) {
this.hls = new Hls();
this.hls.loadSource(hlsUrl);
this.hls.attachMedia(this.videoElement);
this.hls.on(Hls.Events.MANIFEST_PARSED, () => {
this.videoElement.play();
});
this.hls.on(Hls.Events.ERROR, (event, data) => {
console.error('HLS error:', data);
});
} else if (this.videoElement.canPlayType('application/vnd.apple.mpegurl')) {
this.videoElement.src = hlsUrl;
await this.videoElement.play();
} else {
throw new Error('HLS not supported');
}
}
private setupEventListeners() {
this.videoElement.addEventListener('loadstart', () => console.log('Room loading started'));
this.videoElement.addEventListener('canplay', () => console.log('Room ready to play'));
this.videoElement.addEventListener('error', (e) => console.error('Player error:', e));
this.videoElement.addEventListener('waiting', () => console.log('Buffering...'));
this.videoElement.addEventListener('playing', () => console.log('Playback started'));
}
pause() { this.videoElement.pause(); }
resume() { this.videoElement.play(); }
destroy() {
this.hls?.destroy();
this.videoElement.pause();
this.videoElement.src = '';
}
}
Error Handling & Recovery
Common Playback Issues
Copy
Ask AI
class PlaybackErrorHandler {
func handlePlayerError(_ error: Error) {
let errorDescription = error.localizedDescription.lowercased()
if errorDescription.contains("network") {
showAlert("Network Error", message: "Please check your internet connection")
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.retryPlayback()
}
} else if errorDescription.contains("format") {
showAlert("Format Error", message: "Unsupported video format")
} else if errorDescription.contains("permission") {
showAlert("Access Denied", message: "You don't have permission to view this room")
} else {
showAlert("Playback Error", message: error.localizedDescription)
}
}
private func retryPlayback() {
// Implement retry logic
}
}
Concurrency Limit: There is a limitation on the maximum number of concurrent live rooms. Contact community.social.plus.co with your use case to determine if limits can be raised.
Performance Tip: For mobile applications, monitor device temperature and network conditions. Consider reducing video quality during poor network conditions to maintain playback stability.