> ## Documentation Index
> Fetch the complete documentation index at: https://learn.social.plus/llms.txt
> Use this file to discover all available pages before exploring further.

# Playback Overview

> Understand the SDK room playback contract and hand live or recorded playback URLs to your player.

Playback in the social.plus SDK is a room-data workflow. The SDK gives your app room state, live playback URLs, recorded playback metadata, optional URL refresh helpers, and watch-session analytics. Your app owns the media player, autoplay policy, buffering UI, captions, DRM, seek controls, and platform playback SDK.

<Note>
  This page covers SDK playback data for rooms. Use your own player layer, such as AVPlayer, ExoPlayer, a browser video element with HLS support, or another product-approved player, after the SDK returns a playable URL.
</Note>

## Platform Surface

| Platform   | Observe room state                                      | Live source                 | Recorded source                   | Recorded refresh                        | Watch analytics    |
| ---------- | ------------------------------------------------------- | --------------------------- | --------------------------------- | --------------------------------------- | ------------------ |
| TypeScript | `RoomRepository.getRoom(roomId, callback)`              | `room.livePlaybackUrl`      | `room.recordedPlaybackInfos[]`    | `RoomRepository.getRecordedUrl(roomId)` | `room.analytics()` |
| iOS        | `AmityRoomRepository().getRoom(withId:)`                | `room.livePlaybackUrl`      | `room.recordedData[]`             | Re-observe or fetch the room object     | `room.analytics()` |
| Android    | `AmityVideoClient.newRoomRepository().getRoom(roomId)`  | `room.getLivePlaybackUrl()` | `room.getRecordedPlaybackInfos()` | `getRecordedUrls(roomId)`               | `room.analytics()` |
| Flutter    | No current public room playback API found in this audit | Not available               | Not available                     | Not available                           | Not available      |

## Playback Flow

<Steps>
  <Step title="Observe or fetch the room">
    Use a room live object or room query from the platform SDK. Keep the subscription alive while the viewing screen is open.
  </Step>

  <Step title="Choose a playback source">
    Use the live URL only when the room is `live` or `waitingReconnect`. Use recorded metadata only when the room is `recorded`.
  </Step>

  <Step title="Hand the URL to your player">
    Pass the selected URL to your app-owned player. Do not call player APIs that are not part of the social.plus SDK.
  </Step>

  <Step title="Track watch time if needed">
    Create and update a room watch session from `room.analytics()` when your product considers the viewer actively watching.
  </Step>
</Steps>

## Parameters

| Concept                 | TypeScript                                  | iOS                                | Android                                          |
| ----------------------- | ------------------------------------------- | ---------------------------------- | ------------------------------------------------ |
| Room ID                 | `room.roomId`                               | `room.roomId`                      | `room.getRoomId()`                               |
| Room status             | `room.status`                               | `room.status`                      | `room.getStatus()`                               |
| Live URL                | `room.livePlaybackUrl`                      | `room.livePlaybackUrl`             | `room.getLivePlaybackUrl()`                      |
| Recorded URL list       | `room.recordedPlaybackInfos[].url`          | `room.recordedData[].playbackUrl`  | `room.getRecordedPlaybackInfos()[].url`          |
| Recorded thumbnail list | `room.recordedPlaybackInfos[].thumbnailUrl` | `room.recordedData[].thumbnailUrl` | `room.getRecordedPlaybackInfos()[].thumbnailUrl` |
| Live resolution         | `room.liveResolution`                       | `room.liveResolution`              | `room.getLiveResolution()`                       |
| Recorded resolution     | `room.recordedResolution`                   | `room.recordedResolution`          | `room.getRecordedResolution()`                   |
| Watch analytics         | `room.analytics()`                          | `room.analytics()`                 | `room.analytics()`                               |

## Choose a Playback Source

Select a URL from SDK room state before creating or updating your player. Return no source for statuses that are not playable.

<CodeGroup>
  ```typescript TypeScript theme={null}
  function playbackSourceForRoom(room: Amity.Room): string | undefined {
    if (room.status === "live" || room.status === "waitingReconnect") {
      return room.livePlaybackUrl;
    }

    if (room.status === "recorded") {
      return room.recordedPlaybackInfos[0]?.url;
    }

    return undefined;
  }
  ```

  ```kotlin Android theme={null}
  import com.amity.socialcloud.sdk.model.video.room.AmityRoom
  import com.amity.socialcloud.sdk.model.video.room.AmityRoomStatus

  fun playbackSourceForRoom(room: AmityRoom): String? {
      return when (room.getStatus()) {
          AmityRoomStatus.LIVE,
          AmityRoomStatus.WAITING_RECONNECT -> room.getLivePlaybackUrl()
          AmityRoomStatus.RECORDED -> room.getRecordedPlaybackInfos()
              .firstOrNull()
              ?.url
          else -> null
      }
  }
  ```

  ```swift iOS theme={null}
  func playbackSource(for room: AmityRoom) -> String? {
      switch room.status {
      case .live, .waitingReconnect:
          return room.livePlaybackUrl
      case .recorded:
          return room.recordedData.first?.playbackUrl
      default:
          return nil
      }
  }
  ```
</CodeGroup>

## Observe Playback State

Observe room state while the viewer is on a playback screen. A room can move from waiting, to live playback, to ended processing, and later to recorded playback.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { RoomRepository } from "@amityco/ts-sdk";

  function observePlaybackState(roomId: string): Amity.Unsubscriber {
    return RoomRepository.getRoom(roomId, snapshot => {
      if (snapshot.error) {
        handleError(snapshot.error);
        return;
      }

      const room = snapshot.data;
      const source =
        room.status === "live" || room.status === "waitingReconnect"
          ? room.livePlaybackUrl
          : room.status === "recorded"
            ? room.recordedPlaybackInfos[0]?.url
            : undefined;

      if (source) {
        showSuccessMessage(source);
        return;
      }

      showSuccessMessage(room.status);
    });
  }
  ```

  ```kotlin Android theme={null}
  import com.amity.socialcloud.sdk.api.video.AmityVideoClient
  import com.amity.socialcloud.sdk.model.video.room.AmityRoomStatus

  val disposable = AmityVideoClient.newRoomRepository()
      .getRoom(roomId)
      .subscribe(
          { room ->
              when (room.getStatus()) {
                  AmityRoomStatus.LIVE,
                  AmityRoomStatus.WAITING_RECONNECT -> {
                      showSuccessMessage(room.getLivePlaybackUrl() ?: "live")
                  }
                  AmityRoomStatus.RECORDED -> {
                      val source = room.getRecordedPlaybackInfos()
                          .firstOrNull()
                          ?.url

                      showSuccessMessage(source ?: "recorded")
                  }
                  else -> {
                      showSuccessMessage(room.getStatus())
                  }
              }
          },
          { error -> handleGeneralError(error) }
      )
  ```

  ```swift iOS theme={null}
  var roomObservationToken: AmityNotificationToken?
  let roomObject = AmityRoomRepository().getRoom(withId: roomId)

  roomObservationToken = roomObject.observe { liveObject, error in
      if let error {
          handleGeneralError(error)
          return
      }

      guard let room = liveObject.snapshot else { return }

      switch room.status {
      case .live, .waitingReconnect:
          showSuccessMessage(room.livePlaybackUrl)
      case .recorded:
          showSuccessMessage(room.recordedData.first?.playbackUrl)
      default:
          showSuccessMessage(room.status.rawValue)
      }
  }
  ```
</CodeGroup>

## Refresh Recorded Playback

Refresh recorded playback data before starting a recorded video, after a long pause, or when your player reports an expired or unauthorized media URL.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { RoomRepository } from "@amityco/ts-sdk";

  async function refreshRecordedPlaybackUrl(roomId: string): Promise<string> {
    const recordedUrl = await RoomRepository.getRecordedUrl(roomId);

    showSuccessMessage(recordedUrl.expiresAt);

    return recordedUrl.url;
  }
  ```

  ```kotlin Android theme={null}
  import com.amity.socialcloud.sdk.api.video.AmityVideoClient

  val disposable = AmityVideoClient.newRoomRepository()
      .getRecordedUrls(roomId)
      .subscribe(
          { urls -> showSuccessMessage(urls.firstOrNull() ?: "no-recorded-url") },
          { error -> handleGeneralError(error) }
      )
  ```

  ```swift iOS theme={null}
  var refreshToken: AmityNotificationToken?
  let roomObject = AmityRoomRepository().getRoom(withId: roomId)

  refreshToken = roomObject.observeOnce { liveObject, error in
      if let error {
          handleGeneralError(error)
          return
      }

      guard let room = liveObject.snapshot,
            room.status == .recorded else {
          return
      }

      showSuccessMessage(room.recordedData.first?.playbackUrl)
  }
  ```
</CodeGroup>

## Status Handling

| Room status        | Playback behavior                                                                  |
| ------------------ | ---------------------------------------------------------------------------------- |
| `idle`             | Show a waiting or not-started state. Do not create a player source.                |
| `live`             | Use the live playback URL when present.                                            |
| `waitingReconnect` | Keep the viewing UI available and show reconnecting state if playback stalls.      |
| `ended`            | Stop live playback and show processing state while recorded metadata is not ready. |
| `recorded`         | Use recorded playback metadata or the platform refresh helper.                     |
| `terminated`       | TypeScript room status for a terminated room. Do not assume playback is available. |
| `error`            | Show a recoverable error state and let the viewer retry or leave.                  |

<Warning>
  Do not synthesize fallback playback URLs. If the SDK room state does not include a playable live or recorded source for the current viewer, show an unavailable, blocked, processing, or retry state based on your product rules.
</Warning>

## SDK and Player Boundary

| Area        | social.plus SDK owns                                          | Your app/player owns                                                                  |
| ----------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| Discovery   | Room IDs, room post data, room state, live object updates     | Ranking, placement, navigation, and empty states                                      |
| Source data | Live URLs, recorded URLs, thumbnails, and resolution metadata | Player initialization, buffering, seek controls, captions, DRM, and release lifecycle |
| Freshness   | Platform refresh or re-observation helpers                    | Retry timing, expired URL recovery, and player reload                                 |
| Analytics   | Room watch-session APIs                                       | Deciding which player states count as active watch time                               |

## Related Topics

<CardGroup cols={3}>
  <Card title="Live Viewing" icon="tower-broadcast" href="/social-plus-sdk/video-new/broadcasting/live-viewing">
    Observe live room state and hand the live URL to your player.
  </Card>

  <Card title="Recorded Playback" icon="circle-play" href="/social-plus-sdk/video-new/broadcasting/recorded-playback">
    Wait for recorded status, read recorded metadata, and refresh URLs.
  </Card>

  <Card title="Livestream Analytics" icon="chart-line" href="/social-plus-sdk/video-new/analytics/overview">
    Track room watch sessions after playback starts.
  </Card>
</CardGroup>
