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 Status | Recording Available | Playback Method |
|---|
| idle | No | Room not started yet |
| live | No | Use getLivePlaybackUrl() instead |
| ended | No (processing) | Wait for recorded status |
| recorded | Yes | Use 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: