

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

# Menerbitkan & Berlangganan dengan SDK Siaran iOS IVS \| Streaming Real-Time
<a name="ios-publish-subscribe"></a>

Dokumen ini membawa Anda melalui langkah-langkah yang terlibat dalam penerbitan dan berlangganan ke panggung menggunakan SDK siaran iOS streaming real-time IVS.

## Konsep
<a name="ios-publish-subscribe-concepts"></a>

Tiga konsep inti mendasari fungsionalitas real-time: [panggung](#ios-publish-subscribe-concepts-stage), [strategi](#ios-publish-subscribe-concepts-strategy), dan [penyaji](#ios-publish-subscribe-concepts-renderer). Tujuan desain adalah meminimalkan jumlah logika sisi klien yang diperlukan untuk membangun produk yang berfungsi.

### Stage
<a name="ios-publish-subscribe-concepts-stage"></a>

`IVSStage`Kelas adalah titik utama interaksi antara aplikasi host dan SDK. Kelas mewakili panggung itu sendiri dan digunakan untuk bergabung dan meninggalkan panggung. Membuat atau bergabung dengan tahap memerlukan string token yang valid dan belum kedaluwarsa dari bidang kontrol (direpresentasikan sebagai`token`). Bergabung dan meninggalkan panggung itu sederhana.

```
let stage = try IVSStage(token: token, strategy: self)

try stage.join()

stage.leave()
```

`IVSStage`Kelas juga adalah tempat `IVSStageRenderer` dan `IVSErrorDelegate` dapat dilampirkan:

```
let stage = try IVSStage(token: token, strategy: self)
stage.errorDelegate = self
stage.addRenderer(self) // multiple renderers can be added
```

### Strategi
<a name="ios-publish-subscribe-concepts-strategy"></a>

`IVSStageStrategy`Protokol menyediakan cara bagi aplikasi host untuk mengkomunikasikan keadaan tahap yang diinginkan ke SDK. Tiga fungsi perlu diimplementasikan:`shouldSubscribeToParticipant`,`shouldPublishParticipant`, dan`streamsToPublishForParticipant`. Semua dibahas di bawah ini.

#### Berlangganan Peserta
<a name="ios-publish-subscribe-concepts-strategy-participants"></a>

```
func stage(_ stage: IVSStage, shouldSubscribeToParticipant participant: IVSParticipantInfo) -> IVSStageSubscribeType
```

Ketika peserta jarak jauh bergabung dengan tahap, SDK menanyakan aplikasi host tentang status langganan yang diinginkan untuk peserta tersebut. Pilihannya adalah`.none`,`.audioOnly`, dan`.audioVideo`. Saat mengembalikan nilai untuk fungsi ini, aplikasi host tidak perlu khawatir tentang status publikasi, status langganan saat ini, atau status koneksi tahap. Jika `.audioVideo` dikembalikan, SDK menunggu hingga peserta jarak jauh memublikasikan sebelum berlangganan, dan memperbarui aplikasi host melalui perender selama proses berlangsung.

Berikut adalah contoh implementasi:

```
func stage(_ stage: IVSStage, shouldSubscribeToParticipant participant: IVSParticipantInfo) -> IVSStageSubscribeType {
    return .audioVideo
}
```

Ini adalah implementasi lengkap dari fungsi ini untuk aplikasi host yang selalu ingin semua peserta saling bertemu; misalnya, aplikasi obrolan video.

Implementasi yang lebih maju juga dimungkinkan. Gunakan `attributes` properti `IVSParticipantInfo` untuk berlangganan peserta secara selektif berdasarkan atribut yang disediakan server:

```
func stage(_ stage: IVSStage, shouldSubscribeToParticipant participant: IVSParticipantInfo) -> IVSStageSubscribeType {
    switch participant.attributes["role"] {
    case "moderator": return .none
    case "guest": return .audioVideo
    default: return .none
    }
}
```

Ini dapat digunakan untuk membuat panggung di mana moderator dapat memantau semua tamu tanpa terlihat atau didengar sendiri. Aplikasi host dapat menggunakan logika bisnis tambahan untuk membiarkan moderator melihat satu sama lain tetapi tetap tidak terlihat oleh tamu.

#### Konfigurasi untuk Berlangganan Peserta
<a name="ios-publish-subscribe-concepts-strategy-participants-config"></a>

```
func stage(_ stage: IVSStage, subscribeConfigurationForParticipant participant: IVSParticipantInfo) -> IVSSubscribeConfiguration
```

Jika peserta jarak jauh sedang berlangganan (lihat [Berlangganan Peserta](#ios-publish-subscribe-concepts-strategy-participants)), SDK akan menanyakan aplikasi host tentang konfigurasi langganan khusus untuk peserta tersebut. Konfigurasi ini bersifat opsional dan memungkinkan aplikasi host untuk mengontrol aspek-aspek tertentu dari perilaku pelanggan. Untuk informasi tentang apa yang dapat dikonfigurasi, lihat [SubscribeConfiguration](https://aws.github.io/amazon-ivs-web-broadcast/docs/sdk-reference/interfaces/SubscribeConfiguration)di dokumentasi referensi SDK.

Berikut adalah contoh implementasi:

```
func stage(_ stage: IVSStage, subscribeConfigurationForParticipant participant: IVSParticipantInfo) -> IVSSubscribeConfiguration {
    let config = IVSSubscribeConfiguration()

    try! config.jitterBuffer.setMinDelay(.medium())

    return config
}
```

Implementasi ini memperbarui penundaan minimum jitter-buffer untuk semua peserta berlangganan ke preset. `MEDIUM`

Seperti halnya`shouldSubscribeToParticipant`, implementasi yang lebih maju dimungkinkan. Yang diberikan `ParticipantInfo` dapat digunakan untuk memperbarui konfigurasi berlangganan secara selektif untuk peserta tertentu.

Sebaiknya gunakan perilaku default. Tentukan konfigurasi khusus hanya jika ada perilaku tertentu yang ingin Anda ubah.

#### Publikasi
<a name="ios-publish-subscribe-concepts-strategy-publishing"></a>

```
func stage(_ stage: IVSStage, shouldPublishParticipant participant: IVSParticipantInfo) -> Bool
```

Setelah terhubung ke panggung, SDK menanyakan aplikasi host untuk melihat apakah peserta tertentu harus mempublikasikannya. Ini hanya dipanggil pada peserta lokal yang memiliki izin untuk mempublikasikan berdasarkan token yang disediakan.

Berikut adalah contoh implementasi:

```
func stage(_ stage: IVSStage, shouldPublishParticipant participant: IVSParticipantInfo) -> Bool {
    return true
}
```

Ini untuk aplikasi obrolan video standar di mana pengguna selalu ingin mempublikasikan. Mereka dapat membisukan dan menonaktifkan audio dan video mereka, untuk langsung disembunyikan atau. seen/heard (Mereka juga dapat menggunakan publish/unpublish, tetapi itu jauh lebih lambat. Mute/unmute lebih disukai untuk kasus penggunaan di mana perubahan visibilitas sering diinginkan.)

#### Memilih Streaming untuk Publikasikan
<a name="ios-publish-subscribe-concepts-strategy-streams"></a>

```
func stage(_ stage: IVSStage, streamsToPublishForParticipant participant: IVSParticipantInfo) -> [IVSLocalStageStream]
```

Saat menerbitkan, ini digunakan untuk menentukan aliran audio dan video apa yang harus dipublikasikan. Ini dibahas secara lebih rinci nanti di [Publish a Media Stream](#ios-publish-subscribe-publish-stream).

#### Memperbarui Strategi
<a name="ios-publish-subscribe-concepts-strategy-updates"></a>

Strategi ini dimaksudkan untuk menjadi dinamis: nilai yang dikembalikan dari salah satu fungsi di atas dapat diubah kapan saja. Misalnya, jika aplikasi host tidak ingin mempublikasikan sampai pengguna akhir mengetuk tombol, Anda dapat mengembalikan variabel dari `shouldPublishParticipant` (sesuatu seperti`hasUserTappedPublishButton`). Ketika variabel itu berubah berdasarkan interaksi oleh pengguna akhir, panggil `stage.refreshStrategy()` untuk memberi sinyal ke SDK bahwa ia harus menanyakan strategi untuk nilai terbaru, hanya menerapkan hal-hal yang telah berubah. Jika SDK mengamati bahwa `shouldPublishParticipant` nilainya telah berubah, SDK akan memulai proses publikasi. Jika kueri SDK dan semua fungsi mengembalikan nilai yang sama seperti sebelumnya, `refreshStrategy` panggilan tidak akan membuat modifikasi apa pun pada tahapan.

Jika nilai pengembalian `shouldSubscribeToParticipant` perubahan dari `.audioVideo` ke`.audioOnly`, aliran video akan dihapus untuk semua peserta dengan nilai yang dikembalikan diubah, jika aliran video ada sebelumnya.

Umumnya, tahap menggunakan strategi untuk menerapkan perbedaan antara strategi sebelumnya dan saat ini secara efisien, tanpa aplikasi host perlu khawatir tentang semua keadaan yang diperlukan untuk mengelolanya dengan benar. Karena itu, anggap menelepon `stage.refreshStrategy()` sebagai operasi yang murah, karena tidak melakukan apa-apa kecuali strateginya berubah.

### Penyaji
<a name="ios-publish-subscribe-concepts-renderer"></a>

`IVSStageRenderer`Protokol mengkomunikasikan keadaan panggung ke aplikasi host. Pembaruan pada UI aplikasi host biasanya dapat didukung sepenuhnya oleh peristiwa yang disediakan oleh perender. Penyaji menyediakan fungsi-fungsi berikut:

```
func stage(_ stage: IVSStage, participantDidJoin participant: IVSParticipantInfo)

func stage(_ stage: IVSStage, participantDidLeave participant: IVSParticipantInfo)

func stage(_ stage: IVSStage, participant: IVSParticipantInfo, didChange publishState: IVSParticipantPublishState)

func stage(_ stage: IVSStage, participant: IVSParticipantInfo, didChange subscribeState: IVSParticipantSubscribeState)

func stage(_ stage: IVSStage, participant: IVSParticipantInfo, didAdd streams: [IVSStageStream])

func stage(_ stage: IVSStage, participant: IVSParticipantInfo, didRemove streams: [IVSStageStream])

func stage(_ stage: IVSStage, participant: IVSParticipantInfo, didChangeMutedStreams streams: [IVSStageStream])

func stage(_ stage: IVSStage, didChange connectionState: IVSStageConnectionState, withError error: Error?)

func stage(_ stage: IVSStage, participant: IVSParticipantInfo, stream: IVSRemoteStageStream, didChangeStreamAdaption adaption: Bool)

func stage(_ stage: IVSStage, participant: IVSParticipantInfo, stream: IVSRemoteStageStream, didChange layers: [IVSRemoteStageStreamLayer])

func stage(_ stage: IVSStage, participant: IVSParticipantInfo, stream: IVSRemoteStageStream, didSelect layer: IVSRemoteStageStreamLayer?, reason: IVSRemoteStageStream.LayerSelectedReason)
```

Tidak diharapkan bahwa informasi yang diberikan oleh penyaji berdampak pada nilai pengembalian strategi. Misalnya, nilai pengembalian `shouldSubscribeToParticipant` tidak diharapkan berubah ketika `participant:didChangePublishState` dipanggil. Jika aplikasi host ingin berlangganan ke peserta tertentu, itu harus mengembalikan jenis langganan yang diinginkan terlepas dari status publikasi peserta tersebut. SDK bertanggung jawab untuk memastikan bahwa keadaan strategi yang diinginkan ditindaklanjuti pada waktu yang tepat berdasarkan keadaan tahap.

Perhatikan bahwa hanya peserta penerbitan yang dipicu`participantDidJoin`, dan setiap kali peserta berhenti menerbitkan atau meninggalkan sesi panggung, `participantDidLeave` dipicu.

## Publikasikan Aliran Media
<a name="ios-publish-subscribe-publish-stream"></a>

Perangkat lokal seperti mikrofon dan kamera internal ditemukan melalui`IVSDeviceDiscovery`. Berikut adalah contoh memilih kamera yang menghadap ke depan dan mikrofon default, lalu mengembalikannya `IVSLocalStageStreams` agar dipublikasikan oleh SDK:

```
let devices = IVSDeviceDiscovery().listLocalDevices()

// Find the camera virtual device, choose the front source, and create a stream
let camera = devices.compactMap({ $0 as? IVSCamera }).first!
let frontSource = camera.listAvailableInputSources().first(where: { $0.position == .front })!
camera.setPreferredInputSource(frontSource)
let cameraStream = IVSLocalStageStream(device: camera)

// Find the microphone virtual device and create a stream
let microphone = devices.compactMap({ $0 as? IVSMicrophone }).first!
let microphoneStream = IVSLocalStageStream(device: microphone)

// Configure the audio manager to use the videoChat preset, which is optimized for bi-directional communication, including echo cancellation.
IVSStageAudioManager.sharedInstance().setPreset(.videoChat)

// This is a function on IVSStageStrategy
func stage(_ stage: IVSStage, streamsToPublishForParticipant participant: IVSParticipantInfo) -> [IVSLocalStageStream] {
    return [cameraStream, microphoneStream]
}
```

## Tampilkan dan Hapus Peserta
<a name="ios-publish-subscribe-participants"></a>

Setelah berlangganan selesai, Anda akan menerima array `IVSStageStream` objek melalui fungsi renderer. `didAddStreams` Untuk melihat pratinjau atau menerima statistik tingkat audio tentang peserta ini, Anda dapat mengakses `IVSDevice` objek yang mendasarinya dari aliran:

```
if let imageDevice = stream.device as? IVSImageDevice {
    let preview = imageDevice.previewView()
    /* attach this UIView subclass to your view */
} else if let audioDevice = stream.device as? IVSAudioDevice {
    audioDevice.setStatsCallback( { stats in
        /* process stats.peak and stats.rms */
    })
}
```

Ketika peserta berhenti menerbitkan atau berhenti berlangganan, `didRemoveStreams` fungsi dipanggil dengan aliran yang telah dihapus. Aplikasi host harus menggunakan ini sebagai sinyal untuk menghapus aliran video peserta dari hierarki tampilan.

`didRemoveStreams`dipanggil untuk semua skenario di mana aliran mungkin dihapus, termasuk:
+ Peserta jarak jauh berhenti menerbitkan.
+ Perangkat lokal berhenti berlangganan atau mengubah langganan dari ke`.audioVideo`. `.audioOnly`
+ Peserta jarak jauh meninggalkan panggung.
+ Peserta lokal meninggalkan panggung.

Karena `didRemoveStreams` dipanggil untuk semua skenario, tidak diperlukan logika bisnis khusus untuk menghapus peserta dari UI selama operasi cuti jarak jauh atau lokal.

## Bisukan dan Bunyikan Streaming Media
<a name="ios-publish-subscribe-mute-streams"></a>

`IVSLocalStageStream`objek memiliki `setMuted` fungsi yang mengontrol apakah aliran diredam. Fungsi ini dapat dipanggil pada aliran sebelum atau sesudah dikembalikan dari fungsi `streamsToPublishForParticipant` strategi.

**Penting**: Jika instance `IVSLocalStageStream` objek baru dikembalikan `streamsToPublishForParticipant` setelah panggilan ke`refreshStrategy`, status bisu objek aliran baru diterapkan ke panggung. Hati-hati saat membuat `IVSLocalStageStream` instance baru untuk memastikan status bisu yang diharapkan dipertahankan.

## Pantau Status Bisu Media Peserta Jarak Jauh
<a name="ios-publish-subscribe-mute-state"></a>

Saat peserta mengubah status bisu aliran video atau audionya, `didChangeMutedStreams` fungsi penyaji dipanggil dengan larik aliran yang telah berubah. Gunakan `isMuted` properti `IVSStageStream` untuk memperbarui UI Anda sesuai:

```
func stage(_ stage: IVSStage, participant: IVSParticipantInfo, didChangeMutedStreams streams: [IVSStageStream]) {
    streams.forEach { stream in 
        /* stream.isMuted */
    }
}
```

## Buat Konfigurasi Panggung
<a name="ios-publish-subscribe-stage-config"></a>

Untuk menyesuaikan nilai konfigurasi video panggung, gunakan`IVSLocalStageStreamVideoConfiguration`:

```
let config = IVSLocalStageStreamVideoConfiguration()
try config.setMaxBitrate(900_000)
try config.setMinBitrate(100_000)
try config.setTargetFramerate(30)
try config.setSize(CGSize(width: 360, height: 640))
config.degradationPreference = .balanced
```

## Dapatkan Statistik WebRTC
<a name="ios-publish-subscribe-webrtc-stats"></a>

Untuk mendapatkan statistik WebRTC terbaru untuk aliran penerbitan atau aliran berlangganan, gunakan terus. `requestRTCStats` `IVSStageStream` Ketika koleksi selesai, Anda akan menerima statistik melalui `IVSStageStreamDelegate` yang dapat diatur`IVSStageStream`. Untuk terus mengumpulkan statistik WebRTC, panggil fungsi ini di file. `Timer`

```
func stream(_ stream: IVSStageStream, didGenerateRTCStats stats: [String : [String : String]]) {
    for stat in stats {
      for member in stat.value {
         print("stat \(stat.key) has member \(member.key) with value \(member.value)")
      }
   }
}
```

## Dapatkan Atribut Peserta
<a name="ios-publish-subscribe-participant-attributes"></a>

Jika Anda menentukan atribut dalam permintaan `CreateParticipantToken` operasi, Anda dapat melihat atribut di `IVSParticipantInfo` properti:

```
func stage(_ stage: IVSStage, participantDidJoin participant: IVSParticipantInfo) {
    print("ID: \(participant.participantId)")
    for attribute in participant.attributes {
        print("attribute: \(attribute.key)=\(attribute.value)")
    }
}
```

## Sematkan Pesan
<a name="ios-publish-subscribe-embed-messages"></a>

`embedMessage`Metode ini IVSImageDevice memungkinkan Anda memasukkan muatan metadata langsung ke bingkai video selama penerbitan. Ini memungkinkan pesan yang disinkronkan bingkai untuk aplikasi waktu nyata. Penyematan pesan hanya tersedia saat menggunakan SDK untuk penerbitan real-time (bukan penerbitan latensi rendah).

Pesan yang disematkan tidak dijamin sampai ke pelanggan karena mereka disematkan langsung dalam bingkai video dan ditransmisikan melalui UDP, yang tidak menjamin pengiriman paket. Kehilangan paket selama transmisi dapat mengakibatkan pesan hilang, terutama dalam kondisi jaringan yang buruk. Untuk mengurangi ini, `embedMessage` metode ini menyertakan `repeatCount` parameter yang menduplikasi pesan di beberapa frame berturut-turut, meningkatkan keandalan pengiriman. Kemampuan ini hanya tersedia untuk streaming video.

### Menggunakan EmbedMessage
<a name="ios-embed-messages-using-embedmessage"></a>

Klien penerbitan dapat menyematkan muatan pesan ke dalam aliran video mereka menggunakan `embedMessage` metode aktif. IVSImageDevice Ukuran muatan harus lebih besar dari 0KB dan kurang dari 1KB. Jumlah pesan tertanam yang dimasukkan per detik tidak boleh melebihi 10KB per detik.

```
let imageDevice: IVSImageDevice = imageStream.device as! IVSImageDevice
let messageData = Data("hello world".utf8)

do {
    try imageDevice.embedMessage(messageData, withRepeatCount: 0)
} catch {
    print("Failed to embed message: \(error)")
}
```

### Mengulangi Muatan Pesan
<a name="ios-embed-messages-repeat-payloads"></a>

Gunakan `repeatCount` untuk menduplikasi pesan di beberapa frame untuk meningkatkan keandalan. Nilai ini harus antara 0 dan 30. Klien yang menerima harus memiliki logika untuk menghapus duplikat pesan.

```
try imageDevice.embedMessage(messageData, withRepeatCount: 5)

// repeatCount: 0-30, receiving clients should handle duplicates
```

### Membaca Pesan Tertanam
<a name="ios-embed-messages-read-messages"></a>

Lihat “Dapatkan Informasi Peningkatan Tambahan (SEI)” di bawah ini untuk cara membaca pesan yang disematkan dari aliran masuk. 

## Dapatkan Informasi Peningkatan Tambahan (SEI)
<a name="ios-publish-subscribe-sei-attributes"></a>

Unit Supplemental Enhancement Information (SEI) NAL digunakan untuk menyimpan metadata yang selaras dengan bingkai di samping video. Klien berlangganan dapat membaca muatan SEI dari penerbit yang menerbitkan H.264 video dengan memeriksa `embeddedMessages` properti pada `IVSImageDeviceFrame` objek yang keluar dari penerbit. `IVSImageDevice` Untuk melakukan ini, dapatkan publisher`IVSImageDevice`, lalu amati setiap frame melalui callback yang disediakan`setOnFrameCallback`, seperti yang ditunjukkan pada contoh berikut:

```
// in an IVSStageRenderer’s stage:participant:didAddStreams: function, after acquiring the new IVSImageStream

let imageDevice: IVSImageDevice? = imageStream.device as? IVSImageDevice
imageDevice?.setOnFrameCallback { frame in
	for message in frame.embeddedMessages {
    		if let seiMessage = message as? IVSUserDataUnregisteredSEIMessage {
        		let seiMessageData = seiMessage.data
        		let seiMessageUUID = seiMessage.UUID

        		// interpret the message's data based on the UUID
    		}
	}
}
```

## Lanjutkan Sesi di Latar Belakang
<a name="ios-publish-subscribe-background-session"></a>

Saat aplikasi memasuki latar belakang, Anda dapat terus berada di panggung sambil mendengar audio jarak jauh, meskipun tidak mungkin untuk terus mengirim gambar dan audio Anda sendiri. Anda perlu memperbarui `IVSStrategy` implementasi Anda untuk berhenti menerbitkan dan berlangganan `.audioOnly` (atau`.none`, jika ada):

```
func stage(_ stage: IVSStage, shouldPublishParticipant participant: IVSParticipantInfo) -> Bool {
    return false
}
func stage(_ stage: IVSStage, shouldSubscribeToParticipant participant: IVSParticipantInfo) -> IVSStageSubscribeType {
    return .audioOnly
}
```

Kemudian buat panggilan ke`stage.refreshStrategy()`.

## Pengkodean Berlapis dengan Simulcast
<a name="ios-publish-subscribe-layered-encoding-simulcast"></a>

Layered encoding dengan simulcast adalah fitur streaming real-time IVS yang memungkinkan penerbit mengirim beberapa lapisan video berkualitas berbeda, dan pelanggan untuk mengonfigurasi lapisan tersebut secara dinamis atau manual. Fitur ini dijelaskan lebih lanjut dalam dokumen [Pengoptimalan Streaming](real-time-streaming-optimization.md).

### Mengkonfigurasi Layered Encoding (Publisher)
<a name="ios-layered-encoding-simulcast-configure-publisher"></a>

Sebagai penerbit, untuk mengaktifkan pengkodean berlapis dengan simulcast, tambahkan konfigurasi berikut ke instantiasi saat Anda: `IVSLocalStageStream`

```
// Enable Simulcast
let config = IVSLocalStageStreamVideoConfiguration()
config.simulcast.enabled = true

let cameraStream = IVSLocalStageStream(device: camera, configuration: config)

// Other Stage implementation code
```

*Bergantung pada resolusi yang Anda tetapkan pada konfigurasi video, sejumlah lapisan akan dikodekan dan dikirim seperti yang didefinisikan di bagian [Lapisan Default, Kualitas, dan Framerates](real-time-streaming-optimization.md#real-time-streaming-optimization-default-layers) dari Pengoptimalan Streaming.*

Selain itu, Anda dapat secara opsional mengonfigurasi lapisan individual dari dalam konfigurasi simulcast:

```
// Enable Simulcast
let config = IVSLocalStageStreamVideoConfiguration()
config.simulcast.enabled = true

let layers = [
    IVSStagePresets.simulcastLocalLayer().default720(),
    IVSStagePresets.simulcastLocalLayer().default180()
]

try config.simulcast.setLayers(layers)

let cameraStream = IVSLocalStageStream(device: camera, configuration: config)

// Other Stage implementation code
```

Sebagai alternatif, Anda dapat membuat konfigurasi lapisan kustom Anda sendiri hingga tiga lapisan. Jika Anda memberikan array kosong atau tidak ada nilai, default yang dijelaskan di atas digunakan. Lapisan dijelaskan dengan setter properti wajib berikut:
+ `setSize: CGSize;`
+ `setMaxBitrate: integer;`
+ `setMinBitrate: integer;`
+ `setTargetFramerate: float;`

Mulai dari preset, Anda dapat mengganti properti individual atau membuat konfigurasi yang sama sekali baru:

```
// Enable Simulcast
let config = IVSLocalStageStreamVideoConfiguration()
config.simulcast.enabled = true

let customHiLayer = IVSStagePresets.simulcastLocalLayer().default720()
try customHiLayer.setTargetFramerate(15)

let layers = [
    customHiLayer,
    IVSStagePresets.simulcastLocalLayer().default180()
]

try config.simulcast.setLayers(layers)

let cameraStream = IVSLocalStageStream(device: camera, configuration: config)

// Other Stage implementation code
```

Untuk nilai maksimum, batas, dan kesalahan yang dapat dipicu saat mengonfigurasi lapisan individual, lihat dokumentasi referensi SDK.

### Mengkonfigurasi Layered Encoding (Subscriber)
<a name="ios-layered-encoding-simulcast-configure-subscriber"></a>

Sebagai pelanggan, tidak ada yang diperlukan untuk mengaktifkan pengkodean berlapis. Jika penerbit mengirim lapisan simulcast, maka secara default server secara dinamis beradaptasi antara lapisan untuk memilih kualitas optimal berdasarkan perangkat pelanggan dan kondisi jaringan.

Atau, untuk memilih lapisan eksplisit yang dikirimkan penerbit, ada beberapa opsi, yang dijelaskan di bawah ini.

### Opsi 1: Preferensi Kualitas Lapisan Awal
<a name="ios-layered-encoding-simulcast-layer-quality-preference"></a>

Dengan menggunakan `subscribeConfigurationForParticipant` strategi, dimungkinkan untuk memilih lapisan awal apa yang ingin Anda terima sebagai pelanggan:

```
func stage(_ stage: IVSStage, subscribeConfigurationForParticipant participant: IVSParticipantInfo) -> IVSSubscribeConfiguration {
    let config = IVSSubscribeConfiguration()

    config.simulcast.initialLayerPreference = .lowestQuality

    return config
}
```

Secara default, pelanggan selalu dikirim lapisan kualitas terendah terlebih dahulu; ini perlahan naik ke lapisan kualitas tertinggi. Ini mengoptimalkan konsumsi bandwidth pengguna akhir dan memberikan waktu terbaik untuk video, mengurangi pembekuan video awal bagi pengguna di jaringan yang lebih lemah.

Opsi ini tersedia untuk`InitialLayerPreference`:
+ `lowestQuality`— Server memberikan lapisan video dengan kualitas terendah terlebih dahulu. Ini mengoptimalkan konsumsi bandwidth, serta waktu ke media. Kualitas didefinisikan sebagai kombinasi ukuran, bitrate, dan framerate video. Misalnya, video 720p memiliki kualitas lebih rendah dari video 1080p.
+ `highestQuality`— Server memberikan lapisan video kualitas tertinggi terlebih dahulu. Ini mengoptimalkan kualitas tetapi dapat meningkatkan waktu ke media. Kualitas didefinisikan sebagai kombinasi ukuran, bitrate, dan framerate video. Misalnya, video 1080p berkualitas lebih tinggi dari video 720p.

**Catatan:** Agar preferensi lapisan awal (`initialLayerPreference`panggilan) diterapkan, berlangganan ulang diperlukan karena pembaruan ini tidak berlaku untuk langganan aktif.

### Opsi 2: Lapisan Pilihan untuk Stream
<a name="ios-layered-encoding-simulcast-preferred-layer"></a>

Metode `preferredLayerForStream` strategi memungkinkan Anda memilih lapisan setelah aliran dimulai. Metode strategi ini menerima peserta dan informasi aliran, sehingga Anda dapat memilih lapisan berdasarkan partisipan per peserta. SDK memanggil metode ini sebagai respons terhadap peristiwa tertentu, seperti ketika lapisan aliran berubah, status peserta berubah, atau aplikasi host menyegarkan strategi.

Metode strategi mengembalikan `IVSRemoteStageStreamLayer` objek, yang dapat menjadi salah satu dari berikut:
+ Objek layer, seperti yang dikembalikan oleh`IVSRemoteStageStream.layers`.
+ null, yang menunjukkan bahwa tidak ada lapisan yang harus dipilih dan adaptasi dinamis lebih disukai.

Misalnya, strategi berikut akan selalu membuat pengguna memilih lapisan video berkualitas terendah yang tersedia:

```
func stage(_ stage: IVSStage, participant: IVSParticipantInfo, preferredLayerFor stream: IVSRemoteStageStream) -> IVSRemoteStageStreamLayer? {
    return stream.lowestQualityLayer
}
```

Untuk mengatur ulang pemilihan lapisan dan kembali ke adaptasi dinamis, kembalikan null atau undefined dalam strategi. Dalam contoh ini, `appState` adalah variabel placeholder yang mewakili status aplikasi host.

```
func stage(_ stage: IVSStage, participant: IVSParticipantInfo, preferredLayerFor stream: IVSRemoteStageStream) -> IVSRemoteStageStreamLayer? {
    If appState.isAutoMode {
        return nil
    } else {
        return appState.layerChoice
    }
}
```

### Opsi 3: Pembantu RemoteStageStream Lapisan
<a name="ios-layered-encoding-simulcast-remotestagestream-helpers"></a>

`IVSRemoteStageStream`memiliki beberapa pembantu yang dapat digunakan untuk membuat keputusan tentang pemilihan lapisan dan menampilkan pilihan yang sesuai untuk pengguna akhir:
+ **Layer Events** — Di samping itu`IVSStageRenderer`, `IVSRemoteStageStreamDelegate` memiliki peristiwa yang mengkomunikasikan perubahan adaptasi layer dan simulcast:
  + `func stream(_ stream: IVSRemoteStageStream, didChangeAdaption adaption: Bool)`
  + `func stream(_ stream: IVSRemoteStageStream, didChange layers: [IVSRemoteStageStreamLayer])`
  + `func stream(_ stream: IVSRemoteStageStream, didSelect layer: IVSRemoteStageStreamLayer?, reason: IVSRemoteStageStream.LayerSelectedReason)`
+ **Metode Layer** — `IVSRemoteStageStream` memiliki beberapa metode pembantu yang dapat digunakan untuk mendapatkan informasi tentang aliran dan lapisan yang disajikan. Metode ini tersedia di aliran jarak jauh yang disediakan dalam `preferredLayerForStream` strategi, serta aliran jarak jauh yang diekspos melalui`func stage(_ stage: IVSStage, participant: IVSParticipantInfo, didAdd streams: [IVSStageStream])`.
  + `stream.layers`
  + `stream.selectedLayer`
  + `stream.lowestQualityLayer`
  + `stream.highestQualityLayer`
  + `stream.layers(with: IVSRemoteStageStreamLayerConstraints)`

Untuk detailnya, lihat `IVSRemoteStageStream` kelas dalam [dokumentasi referensi SDK](https://aws.github.io/amazon-ivs-broadcast-docs/latest/ios/). Untuk `LayerSelected` alasannya, jika `UNAVAILABLE` dikembalikan, ini menunjukkan bahwa lapisan yang diminta tidak dapat dipilih. Pilihan upaya terbaik dilakukan sebagai gantinya, yang biasanya merupakan lapisan kualitas yang lebih rendah untuk menjaga stabilitas aliran.

## Siarkan Panggung ke Saluran IVS
<a name="ios-publish-subscribe-broadcast-stage"></a>

Untuk menyiarkan panggung, buat yang terpisah `IVSBroadcastSession` dan kemudian ikuti instruksi biasa untuk penyiaran dengan SDK, yang dijelaskan di atas. `device`Properti pada `IVSStageStream` akan berupa `IVSImageDevice` atau `IVSAudioDevice` seperti yang ditunjukkan pada cuplikan di atas; ini dapat dihubungkan ke `IVSBroadcastSession.mixer` untuk menyiarkan seluruh tahap dalam tata letak yang dapat disesuaikan.

Secara opsional, Anda dapat menggabungkan panggung dan menyiarkannya ke saluran latensi rendah IVS, untuk menjangkau audiens yang lebih besar. Lihat [Mengaktifkan Beberapa Host di Amazon IVS Stream di Panduan Pengguna Low-Latency Streaming](https://docs.aws.amazon.com//ivs/latest/LowLatencyUserGuide/multiple-hosts.html) IVS.