> ## 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.

# Live Room Viewing

> Discover room posts, observe room playback state, and hand SDK playback URLs to your player.

Live room viewing starts from a room ID or a room post. The social.plus SDK owns room discovery, room status, live playback URLs, recorded playback metadata, and live room post collections. Your app owns the actual video player, buffering UI, autoplay policy, and platform playback SDK.

<Note>
  This page covers SDK room viewing data. AVPlayer, ExoPlayer, HLS.js, browser autoplay handling, DRM, and player UI are app-owned concerns after the SDK returns a playback URL.
</Note>

## Platform Surface

| Platform   | Find room posts                                                                   | Observe room                                           | Live playback field         | Recorded playback field           |
| ---------- | --------------------------------------------------------------------------------- | ------------------------------------------------------ | --------------------------- | --------------------------------- |
| TypeScript | `PostRepository.getLiveRoomPosts(...)`, `getCommunityLiveRoomPosts(...)`          | `RoomRepository.getRoom(roomId, callback)`             | `room.livePlaybackUrl`      | `room.recordedPlaybackInfos[]`    |
| iOS        | `AmityPostRepository().getLiveRoomPosts()`, `getCommunityLiveRoomPosts(withIds:)` | `AmityRoomRepository().getRoom(withId:)`               | `room.livePlaybackUrl`      | `room.recordedData[]`             |
| Android    | `AmityPostRepository.getLiveRoomPosts()`, `getCommunityLiveRoomPosts(...)`        | `AmityVideoClient.newRoomRepository().getRoom(roomId)` | `room.getLivePlaybackUrl()` | `room.getRecordedPlaybackInfos()` |
| Flutter    | No current public room viewing API found in this audit                            | Not available                                          | Not available               | Not available                     |

## Parameters

| Concept                   | Platforms                | TypeScript                                    | iOS                                              | Android                                                |
| ------------------------- | ------------------------ | --------------------------------------------- | ------------------------------------------------ | ------------------------------------------------------ |
| Live room post collection | TypeScript, iOS, Android | `getLiveRoomPosts(callback)`                  | `getLiveRoomPosts()`                             | `getLiveRoomPosts()`                                   |
| Community live room posts | TypeScript, iOS, Android | `getCommunityLiveRoomPosts({ communityIds })` | `getCommunityLiveRoomPosts(withIds:)`            | `getCommunityLiveRoomPosts(communityIds)`              |
| Room ID in post data      | TypeScript, iOS, Android | `post.data.roomId` when `dataType === "room"` | `post.data?["roomId"]` when `dataType == "room"` | `AmityPost.Data.ROOM.getRoomId()`                      |
| Observe room              | TypeScript, iOS, Android | `RoomRepository.getRoom(roomId, callback)`    | `AmityRoomRepository().getRoom(withId:)`         | `AmityVideoClient.newRoomRepository().getRoom(roomId)` |
| Live URL                  | TypeScript, iOS, Android | `room.livePlaybackUrl`                        | `room.livePlaybackUrl`                           | `room.getLivePlaybackUrl()`                            |
| Recorded URL              | TypeScript, iOS, Android | `room.recordedPlaybackInfos[].url`            | `room.recordedData[].playbackUrl`                | `room.getRecordedPlaybackInfos()[].url`                |

## Find Live Room Posts

Use live room post collections when your product shows a live shelf or a community-specific live section. Extract the `roomId` from room post data, then observe the room itself for playback state.

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

  function observeCommunityLiveRoomPosts(communityId: string): Amity.Unsubscriber {
    return PostRepository.getCommunityLiveRoomPosts(
      { communityIds: [communityId] },
      snapshot => {
        if (snapshot.error) {
          handleError(snapshot.error);
          return;
        }

        snapshot.data.forEach(post => {
          const roomData = post.data as { roomId?: string } | undefined;

          if (post.dataType === "room" && roomData?.roomId) {
            showSuccessMessage(roomData.roomId);
          }
        });
      },
    );
  }
  ```

  ```kotlin Android theme={null}
  import com.amity.socialcloud.sdk.api.social.AmitySocialClient
  import com.amity.socialcloud.sdk.model.social.post.AmityPost

  val disposable = AmitySocialClient.newPostRepository()
      .getCommunityLiveRoomPosts(listOf(communityId))
      .subscribe(
          { posts ->
              posts.forEach { post ->
                  val roomData = post.getData()

                  if (roomData is AmityPost.Data.ROOM) {
                      showSuccessMessage(roomData.getRoomId())
                  }
              }
          },
          { error -> handleGeneralError(error) }
      )
  ```

  ```swift iOS theme={null}
  var liveRoomPostsToken: AmityNotificationToken?
  let liveRoomPosts = AmityPostRepository()
      .getCommunityLiveRoomPosts(withIds: [communityId])

  liveRoomPostsToken = liveRoomPosts.observe { collection, error in
      if let error {
          handleGeneralError(error)
          return
      }

      collection.snapshots.forEach { post in
          if post.dataType == "room",
             let roomId = post.data?["roomId"] as? String {
              showSuccessMessage(roomId)
          }
      }
  }
  ```
</CodeGroup>

## Observe Room Playback State

Observe the room record while the viewing screen is open. A live object update can move the UI from waiting, to live playback, to ended, and later to recorded playback.

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

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

      const room = snapshot.data;

      if (room.status === "live" && room.livePlaybackUrl) {
        showSuccessMessage(room.livePlaybackUrl);
        return;
      }

      if (room.status === "recorded") {
        showSuccessMessage(room.recordedPlaybackInfos[0]?.url);
        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 -> {
                      room.getLivePlaybackUrl()?.let { showSuccessMessage(it) }
                  }
                  AmityRoomStatus.RECORDED -> {
                      room.getRecordedPlaybackInfos()
                          .firstOrNull()
                          ?.url
                          ?.let { showSuccessMessage(it) }
                  }
                  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>

## Choose a Playback Source

Keep playback source selection small and deterministic. Pass the selected URL to your own player layer only when the SDK room state has a playable source.

<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>

## Status Handling

| Status             | Viewer behavior                                                                      |
| ------------------ | ------------------------------------------------------------------------------------ |
| `idle`             | Show a waiting or scheduled state.                                                   |
| `live`             | Use the live playback URL when present.                                              |
| `waitingReconnect` | Keep the player UI available, but show reconnecting state if playback stalls.        |
| `ended`            | Stop live playback and show a processing state while recorded playback is not ready. |
| `recorded`         | Use recorded playback metadata.                                                      |
| `error`            | Show a recoverable error state and let the user retry or leave.                      |

<Info>
  If a room is live but the SDK returns no live playback URL, do not invent a fallback URL. Treat it as unavailable for the current viewer and show a product-specific blocked, unavailable, or retry state.
</Info>

## Player Boundary

| Area            | Owned by social.plus SDK                         | Owned by your app/player                                      |
| --------------- | ------------------------------------------------ | ------------------------------------------------------------- |
| Discovery       | Live room post collections and room IDs          | Placement, ranking, and empty states                          |
| State           | Room status and live object updates              | Player state machine and user-facing copy                     |
| Playback source | `livePlaybackUrl` and recorded playback metadata | AVPlayer, ExoPlayer, HLS.js, web video, buffering, and errors |
| Cleanup         | Observer unsubscribe, token release, Rx disposal | Player teardown and audio/video session cleanup               |

## Related Topics

<CardGroup cols={3}>
  <Card title="Start Broadcasting" icon="radio" href="./start-broadcasting">
    Fetch broadcaster credentials and start the host-side media session.
  </Card>

  <Card title="Manage Rooms" icon="settings" href="./manage-rooms">
    Observe, stop, update, or delete room records.
  </Card>

  <Card title="Recorded Playback" icon="play" href="./recorded-playback">
    Handle recorded playback after a live room ends.
  </Card>
</CardGroup>
