View a markdown version of this page

Publicación y suscripción con el SDK de transmisión para iOS de IVS - Amazon IVS

Publicación y suscripción con el SDK de transmisión para iOS de IVS

En esta sección se explican los pasos necesarios para publicar y suscribirse a una fase mediante una aplicación iOS.

Creación de vistas

Lo primero es usar el archivo ViewController.swift creado automáticamente para importar AmazonIVSBroadcast y, luego, agregar algunas @IBOutlets que vincular:

import AmazonIVSBroadcast class ViewController: UIViewController { @IBOutlet private var textFieldToken: UITextField! @IBOutlet private var buttonJoin: UIButton! @IBOutlet private var labelState: UILabel! @IBOutlet private var switchPublish: UISwitch! @IBOutlet private var collectionViewParticipants: UICollectionView!

Ahora creamos esas vistas y las vinculamos en Main.storyboard. Esta es la estructura de vistas que utilizaremos:

Use Main.storyboard para crear una vista de iOS.

Para configurar AutoLayout, tenemos que personalizar tres vistas. La primera vista es Vista de colección de participantes (una UICollectionView). Alinee InicialFinal e Inferior con respecto a Área segura. Alinee también Superior con respecto a Contenedor de controles.

Personalice la Vista de colección de participantes de iOS.

La segunda vista es Contenedor de controles. Alinee InicialFinal y Superior con respecto a Área segura:

Personalice la vista Contenedor de controles de iOS.

La tercera y última vista es Vista de pila vertical. Alinee Superior, Inicial, Final e Inferior con respecto a Supervista. En cuanto al diseño, establezca el espaciado en 8 en lugar de en 0.

Personalice la vista de pila vertical de iOS.

Las UIStackViews se encargarán del diseño de las vistas restantes. En las tres UIStackViews, seleccione la opción Relleno para Alineación y Distribución.

Personalice el resto de las vistas de iOS con UIStackViews.

Por último, vinculemos estas vistas a nuestro ViewController. Desde arriba, asigne las siguientes vistas:

  • El campo de texto de unión se asigna a textFieldToken.

  • El botón de unión se asigna a buttonJoin.

  • La etiqueta de estado se asigna a labelState.

  • El botón de publicación se asigna a switchPublish.

  • Vista de colección de participantes se asigna a collectionViewParticipants.

Aproveche también para establecer el dataSource del elemento Vista de colección de participantes en el ViewController propietario:

Configure el dataSource de Vista de colección de participantes de la aplicación de iOS.

Ahora creamos la subclase UICollectionViewCell en la que se renderizarán los participantes. Lo primero es crear un nuevo archivo Cocoa Touch Class:

Cree una UICollectionViewCell para renderizar los participantes de iOS en tiempo real.

Asígnele el nombre ParticipantUICollectionViewCell y conviértala en una subclase de UICollectionViewCell en Swift. Volvemos al archivo de Swift y creamos @IBOutlets que vincular:

import AmazonIVSBroadcast class ParticipantCollectionViewCell: UICollectionViewCell { @IBOutlet private var viewPreviewContainer: UIView! @IBOutlet private var labelParticipantId: UILabel! @IBOutlet private var labelSubscribeState: UILabel! @IBOutlet private var labelPublishState: UILabel! @IBOutlet private var labelVideoMuted: UILabel! @IBOutlet private var labelAudioMuted: UILabel! @IBOutlet private var labelAudioVolume: UILabel!

En el archivo XIB asociado, cree esta jerarquía de vistas:

Cree una jerarquía de vistas de iOS en el archivo XIB asociado.

En AutoLayout, volveremos a modificar tres vistas. La primera vista es Vista previa de contenedores. Alinee Final, Inicial, Superior e Inferior con respecto a Celda de vista de colección de participantes.

Personalice la Vista previa de contenedores de iOS.

La segunda vista es Vista. Alinee Inicial y Superior con respecto a Celda de vista de colección de participantes y cambie el valor a 4.

Personalice la vista de iOS.

La tercera vista es Vista de pila. Alinee Final, Inicial, Superior e Inferior con respecto a Supervista y cambie el valor a 4.

Personalice la vista de pila de iOS.

Permisos y temporizador de inactividad

En ViewController, tenemos que desactivar el temporizador de inactividad del sistema para evitar que el dispositivo entre en estado de suspensión mientras se esté utilizando nuestra aplicación:

override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) // Prevent the screen from turning off during a call. UIApplication.shared.isIdleTimerDisabled = true } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) UIApplication.shared.isIdleTimerDisabled = false }

A continuación, solicitamos los permisos de cámara y micrófono del sistema:

private func checkPermissions() { checkOrGetPermission(for: .video) { [weak self] granted in guard granted else { print("Video permission denied") return } self?.checkOrGetPermission(for: .audio) { [weak self] granted in guard granted else { print("Audio permission denied") return } self?.setupLocalUser() // we will cover this later } } } private func checkOrGetPermission(for mediaType: AVMediaType, _ result: @escaping (Bool) -> Void) { func mainThreadResult(_ success: Bool) { DispatchQueue.main.async { result(success) } } switch AVCaptureDevice.authorizationStatus(for: mediaType) { case .authorized: mainThreadResult(true) case .notDetermined: AVCaptureDevice.requestAccess(for: mediaType) { granted in mainThreadResult(granted) } case .denied, .restricted: mainThreadResult(false) @unknown default: mainThreadResult(false) } }

Estado de la aplicación

Tenemos que configurar collectionViewParticipants con el archivo de diseño que creamos anteriormente:

override func viewDidLoad() { super.viewDidLoad() // We render everything to exactly the frame, so don't allow scrolling. collectionViewParticipants.isScrollEnabled = false collectionViewParticipants.register(UINib(nibName: "ParticipantCollectionViewCell", bundle: .main), forCellWithReuseIdentifier: "ParticipantCollectionViewCell") }

Para representar a cada participante, creamos una estructura simple llamada StageParticipant. Esta puede incluirse en el archivo ViewController.swift, aunque también se puede crear un archivo nuevo.

import Foundation import AmazonIVSBroadcast struct StageParticipant { let isLocal: Bool var participantId: String? var publishState: IVSParticipantPublishState = .notPublished var subscribeState: IVSParticipantSubscribeState = .notSubscribed var streams: [IVSStageStream] = [] init(isLocal: Bool, participantId: String?) { self.isLocal = isLocal self.participantId = participantId } }

Para hacer un seguimiento de esos participantes, los guardamos en una matriz que sea una propiedad privada de ViewController:

private var participants = [StageParticipant]()

Esta propiedad se utilizará para facilitar información al UICollectionViewDataSource que enlazamos en el guion gráfico anterior:

extension ViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return participants.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ParticipantCollectionViewCell", for: indexPath) as? ParticipantCollectionViewCell { cell.set(participant: participants[indexPath.row]) return cell } else { fatalError("Couldn't load custom cell type 'ParticipantCollectionViewCell'") } } }

Para ver su propia vista previa antes de unirse a un escenario, creamos inmediatamente un participante local:

override func viewDidLoad() { /* existing UICollectionView code */ participants.append(StageParticipant(isLocal: true, participantId: nil)) }

Esto hace que la celda de un participante se renderice inmediatamente cuando se ejecute la aplicación. La celda representa al participante local.

Los usuarios quieren poder verse antes de unirse a un escenario, por lo que, a continuación, implementamos el método setupLocalUser(). El código de gestión de permisos anterior se encarga de llamar a este método. Almacenamos la referencia de la cámara y el micrófono como objetos de IVSLocalStageStream.

private var streams = [IVSLocalStageStream]() private let deviceDiscovery = IVSDeviceDiscovery() private func setupLocalUser() { // Gather our camera and microphone once permissions have been granted let devices = deviceDiscovery.listLocalDevices() streams.removeAll() if let camera = devices.compactMap({ $0 as? IVSCamera }).first { streams.append(IVSLocalStageStream(device: camera)) // Use a front camera if available. if let frontSource = camera.listAvailableInputSources().first(where: { $0.position == .front }) { camera.setPreferredInputSource(frontSource) } } if let mic = devices.compactMap({ $0 as? IVSMicrophone }).first { streams.append(IVSLocalStageStream(device: mic)) } participants[0].streams = streams participantsChanged(index: 0, changeType: .updated) }

Con esto detectamos la cámara y el micrófono del dispositivo a través del SDK y los almacenamos en nuestro objeto streams local. A continuación, asignamos la matriz streams del primer participante (el participante local que creamos anteriormente) a streams. Por último, llamamos a participantsChanged con un index de 0 y un changeType con el valor updated. Esta es una función auxiliar para actualizar UICollectionView con bonitas animaciones. Este es el resultado:

private func participantsChanged(index: Int, changeType: ChangeType) { switch changeType { case .joined: collectionViewParticipants?.insertItems(at: [IndexPath(item: index, section: 0)]) case .updated: // Instead of doing reloadItems, just grab the cell and update it ourselves. It saves a create/destroy of a cell // and more importantly fixes some UI flicker. We disable scrolling so the index path per cell // never changes. if let cell = collectionViewParticipants?.cellForItem(at: IndexPath(item: index, section: 0)) as? ParticipantCollectionViewCell { cell.set(participant: participants[index]) } case .left: collectionViewParticipants?.deleteItems(at: [IndexPath(item: index, section: 0)]) } }

No se preocupe por cell.set aún, lo veremos luego, pero ahí es donde renderizaremos el contenido de la celda en función del participante.

ChangeType es una enumeración simple:

enum ChangeType { case joined, updated, left }

Lo último es ver si el escenario está conectado. Usamos un simple valor bool para comprobarlo. Cuando este valor se actualice, actualizará automáticamente nuestra interfaz de usuario.