Skip to main content

Recorded Room Playback

This guide covers how to access and play recorded rooms after a live broadcast ends, including handling recording availability, implementing video players with seek controls, and managing recordings across all platforms.

Understanding Recording Status

Recording Lifecycle

When a live room ends, recordings go through a processing phase:
Room StatusRecording AvailablePlayback Method
idleNoRoom not started yet
liveNoUse getLivePlaybackUrl() instead
endedNo (processing)Wait for recorded status
recordedYesUse getRecordedPlaybackInfos()

Check Recording Availability

import AmitySDK

class RecordingChecker {
    
    func checkRecordingAvailability(roomId: String) {
        let roomRepository = AmityVideoClient.newRoomRepository()
        let liveRoom = roomRepository.getRoom(roomId)
        
        liveRoom.observeOnce { liveObject, error in
            guard let room = liveObject.object else {
                print("Room not found")
                return
            }
            
            switch room.status {
            case .recorded:
                let recordedInfos = room.getRecordedPlaybackInfos()
                if !recordedInfos.isEmpty {
                    print("Recording available: \(recordedInfos.count) segments")
                    self.playRecording(infos: recordedInfos)
                }
            case .ended:
                print("Room ended, recording is being processed...")
                self.showProcessingMessage()
            case .live:
                print("Room is still live")
            case .idle:
                print("Room hasn't started yet")
            default:
                break
            }
        }
    }
    
    private func playRecording(infos: [AmityRecordedPlaybackInfo]) {
        // Handle recording playback
    }
    
    private func showProcessingMessage() {
        // Show UI indicating recording is processing
    }
}

Accessing Recorded Playback

Get Recorded Playback Info

Use getRecordedPlaybackInfos() to retrieve recording segments:
class RecordedRoomPlayer {
    
    func getRecordedPlaybackInfo(from room: AmityRoom) -> [AmityRecordedPlaybackInfo] {
        guard room.status == .recorded else {
            print("Recording not available yet")
            return []
        }
        
        let recordedInfos = room.getRecordedPlaybackInfos()
        
        // Log recording details
        for (index, info) in recordedInfos.enumerated() {
            print("Segment \(index + 1):")
            print("  URL: \(info.url)")
            print("  Duration: \(info.duration) seconds")
            print("  Resolution: \(info.width) x \(info.height)")
        }
        
        return recordedInfos
    }
    
    func playFirstRecording(from room: AmityRoom) {
        let infos = getRecordedPlaybackInfo(from: room)
        
        guard let firstInfo = infos.first else {
            print("No recordings available")
            return
        }
        
        playRecording(url: firstInfo.url, duration: firstInfo.duration)
    }
    
    private func playRecording(url: String, duration: TimeInterval) {
        guard let videoURL = URL(string: url) else { return }
        
        let player = AVPlayer(url: videoURL)
        let playerViewController = AVPlayerViewController()
        playerViewController.player = player
        
        // Enable seek controls for recorded content
        playerViewController.showsPlaybackControls = true
        
        present(playerViewController, animated: true) {
            player.play()
        }
    }
}

Multiple Recording Segments

Some rooms may have multiple recording segments. Handle them appropriately:
class MultiSegmentPlayer {
    private var player: AVQueuePlayer?
    private var playerItems: [AVPlayerItem] = []
    
    func playAllSegments(recordedInfos: [AmityRecordedPlaybackInfo]) {
        playerItems = recordedInfos.compactMap { info in
            guard let url = URL(string: info.url) else { return nil }
            return AVPlayerItem(url: url)
        }
        
        player = AVQueuePlayer(items: playerItems)
        
        let playerViewController = AVPlayerViewController()
        playerViewController.player = player
        
        present(playerViewController, animated: true) {
            self.player?.play()
        }
    }
}

Error Handling

class RecordingErrorHandler {
    
    func handleError(_ error: Error, for room: AmityRoom) {
        let errorDescription = error.localizedDescription.lowercased()
        
        if errorDescription.contains("not found") || errorDescription.contains("404") {
            // Recording may have been deleted or not available
            showError("Recording Not Available", message: "This recording is no longer available")
        } else if errorDescription.contains("network") {
            showError("Network Error", message: "Please check your connection and try again")
        } else if errorDescription.contains("expired") {
            // Recording URL expired, refresh it
            refreshRecordingUrl(room: room)
        } else {
            showError("Playback Error", message: error.localizedDescription)
        }
    }
    
    private func refreshRecordingUrl(room: AmityRoom) {
        // Re-fetch room to get fresh recording URLs
        let roomRepository = AmityVideoClient.newRoomRepository()
        let liveRoom = roomRepository.getRoom(room.roomId)
        
        liveRoom.observeOnce { liveObject, error in
            guard let freshRoom = liveObject.object else { return }
            let newInfos = freshRoom.getRecordedPlaybackInfos()
            // Retry playback with new URLs
        }
    }
}
Recording Storage: Recordings are stored based on your network configuration. Contact support if you need to modify retention policies or storage limits.
Performance: For long recordings, consider implementing progressive loading or chunked playback to reduce initial load times and memory usage.

Next Steps

With recorded playback implemented: