Cas d’utilisation avancés pour le kit SDK de diffusion iOS d’IVS | diffusion à faible latence - Amazon IVS

Cas d’utilisation avancés pour le kit SDK de diffusion iOS d’IVS | diffusion à faible latence

Nous présentons ici quelques cas d’utilisation avancés. Commencez par la configuration de base ci-dessus et continuez ici.

Créer une configuration de diffusion

Ici, nous créons une configuration personnalisée avec deux emplacements de mélangeur qui nous permettent de lier deux sources vidéo au mélangeur. L’un (custom) est en plein écran et disposé derrière l’autre (camera), qui est plus petit et dans le coin inférieur droit. Notez que pour l’ custom emplacement, nous ne définissons pas la position, la taille ou le mode d’aspect. Étant donné que nous ne définissons pas ces paramètres, l’emplacement utilise les paramètres vidéo pour la taille et la position.

let config = IVSBroadcastConfiguration() try config.audio.setBitrate(128_000) try config.video.setMaxBitrate(3_500_000) try config.video.setMinBitrate(500_000) try config.video.setInitialBitrate(1_500_000) try config.video.setSize(CGSize(width: 1280, height: 720)) config.video.defaultAspectMode = .fit config.mixer.slots = [ try { let slot = IVSMixerSlotConfiguration() // Do not automatically bind to a source slot.preferredAudioInput = .unknown // Bind to user image if unbound slot.preferredVideoInput = .userImage try slot.setName("custom") return slot }(), try { let slot = IVSMixerSlotConfiguration() slot.zIndex = 1 slot.aspect = .fill slot.size = CGSize(width: 300, height: 300) slot.position = CGPoint(x: config.video.size.width - 400, y: config.video.size.height - 400) try slot.setName("camera") return slot }() ]

Créer la session de diffusion (version avancée)

Créez une IVSBroadcastSession comme vous l’avez fait dans l’exemple de base, mais fournissez votre configuration personnalisée ici. Indiquez également nil pour le tableau de périphériques, car nous allons les ajouter manuellement.

let broadcastSession = try IVSBroadcastSession( configuration: config, // The configuration we created above descriptors: nil, // We’ll manually attach devices after delegate: self)

Itérer et attacher une caméra

Ici, nous itérons à travers les périphériques d’entrée que le kit SDK a détectés. Le kit SDK ne renverra que les appareils intégrés sur iOS. Même si les périphériques audio Bluetooth sont connectés, ils apparaîtront en tant que périphérique intégré. Pour de plus amples informations, consultez Problèmes connus et solutions de contournement dans le kit SDK de diffusion iOS d’IVS | Diffusion à faible latence.

Une fois que nous avons trouvé un périphérique que nous voulons utiliser, nous appelons attachDevice pour l’attacher :

let frontCamera = IVSBroadcastSession.listAvailableDevices() .filter { $0.type == .camera && $0.position == .front } .first if let camera = frontCamera { broadcastSession.attach(camera, toSlotWithName: "camera") { device, error in // check error } }

Échanger des caméras

// This assumes you’ve kept a reference called `currentCamera` that points to the current camera. let wants: IVSDevicePosition = (currentCamera.descriptor().position == .front) ? .back : .front // Remove the current preview view since the device will be changing. previewView.subviews.forEach { $0.removeFromSuperview() } let foundCamera = IVSBroadcastSession .listAvailableDevices() .first { $0.type == .camera && $0.position == wants } guard let newCamera = foundCamera else { return } broadcastSession.exchangeOldDevice(currentCamera, withNewDevice: newCamera) { newDevice, _ in currentCamera = newDevice if let camera = newDevice as? IVSImageDevice { do { previewView.addSubview(try finalCamera.previewView()) } catch { print("Error creating preview view \(error)") } } }

Créer une source d’entrée personnalisée

Pour entrer des données audio ou image générées par votre appli, utilisez createImageSource ou createAudioSource. Ces deux méthodes créent des périphériques virtuels (IVSCustomImageSource and IVSCustomAudioSource) qui peuvent être liés au mixage comme n’importe quel autre périphérique.

Les périphériques renvoyés par ces deux méthodes acceptent un CMSampleBuffer via la fonction onSampleBuffer :

  • Pour les sources vidéo, le format de pixel doit être kCVPixelFormatType_32BGRA, 420YpCbCr8BiPlanarFullRange ou 420YpCbCr8BiPlanarVideoRange.

  • Pour les sources audio, la mémoire tampon doit contenir des données PCM linéaires.

Vous ne pouvez pas utiliser une AVCaptureSession avec entrée caméra pour alimenter une source d’image personnalisée tout en utilisant une caméra fournie par le kit SDK de diffusion. Si vous souhaitez utiliser plusieurs caméras simultanément, utilisez AVCaptureMultiCamSession et fournissez deux sources d’image personnalisées.

Les sources d’image personnalisées doivent principalement être utilisées avec du contenu statique tel que des images ou avec du contenu vidéo :

let customImageSource = broadcastSession.createImageSource(withName: "video") try broadcastSession.attach(customImageSource, toSlotWithName: "custom")

Contrôler la connectivité réseau

Il arrive souvent que les appareils mobiles perdent temporairement la connectivité réseau et la rétablissent lors des déplacements. Pour cette raison, il est important de contrôler la connectivité réseau de votre appli et de réagir de manière appropriée en cas de changement.

Lorsque la connexion du diffuseur est perdue, l’état du kit SDK de diffusion passe à error, puis à disconnected. Vous serez averti de ces changements par le biais du IVSBroadcastSessionDelegate. Lorsque vous recevez ces changements d’état :

  1. Contrôlez l’état de connectivité de votre appli de diffusion et appelez start avec votre point de terminaison et votre clé de flux une fois votre connexion restaurée.

  2. Important : contrôlez le rappel du délégué d’état et assurez-vous que l’état passe à connected après que vous avez à nouveau start appelé.

Détacher un périphérique

Si vous souhaitez détacher un périphérique et ne pas le remplacer, détachez-le avec IVSDevice ou IVSDeviceDescriptor :

broadcastSession.detachDevice(currentCamera)

Intégration ReplayKit

Pour diffuser l’écran et l’audio système de l’appareil sur iOS, vous devez intégrer à ReplayKit. Le kit SDK de diffusion Amazon IVS facilite l’intégration de ReplayKit à l’aide de IVSReplayKitBroadcastSession. Dans vos RPBroadcastSampleHandler sous-classe, créez une instance de IVSReplayKitBroadcastSession, puis :

  • Démarrer la séance dans broadcastStarted

  • Arrêter la session dans broadcastFinished

L’objet session aura trois sources personnalisées pour les images de l’écran, l’audio de l’appli et l’audio du microphone. Transmettez les CMSampleBuffers fournis dans processSampleBuffer à ces sources personnalisées.

Pour gérer l’orientation du périphérique, vous devez extraire les métadonnées spécifiques à ReplayKIT à partir de l’exemple de tampon. Utilisez le code suivant :

let imageSource = session.systemImageSource; if let orientationAttachment = CMGetAttachment(sampleBuffer, key: RPVideoSampleOrientationKey as CFString, attachmentModeOut: nil) as? NSNumber, let orientation = CGImagePropertyOrientation(rawValue: orientationAttachment.uint32Value) { switch orientation { case .up, .upMirrored: imageSource.setHandsetRotation(0) case .down, .downMirrored: imageSource.setHandsetRotation(Float.pi) case .right, .rightMirrored: imageSource.setHandsetRotation(-(Float.pi / 2)) case .left, .leftMirrored: imageSource.setHandsetRotation((Float.pi / 2)) } }

Il est possible d’intégrer ReplayKit en utilisant IVSBroadcastSession au lieu de IVSReplayKitBroadcastSession. Cependant, la variante spécifique à ReplayKIT a plusieurs modifications pour réduire l’empreinte mémoire interne afin de ne pas dépasser le plafond de mémoire d’Apple pour les extensions de diffusion.

Pour évaluer la connexion de votre utilisateur avant de démarrer une diffusion, utilisez IVSBroadcastSession.recommendedVideoSettings pour exécuter un bref test. Au fur et à mesure que le test s’exécute, vous recevrez plusieurs recommandations, classées de la plus recommandée à la moins recommandée. Dans cette version du kit SDK, il n’est pas possible de reconfigurer la IVSBroadcastSession actuelle, vous devrez donc la libérer, puis en créer une nouvelle avec les paramètres recommandés. Vous continuerez à recevoir IVSBroadcastSessionTestResults jusqu’à ce que le result.status soit Success ou Error. Vous pouvez vérifier la progression avec result.progress.

Amazon IVS prend en charge un débit binaire maximal de 8,5 Mbit/s (pour les canaux dont le type est STANDARD ou ADVANCED), de sorte que le maximumBitrate renvoyé par cette méthode ne dépasse jamais 8,5 Mbit/s. Pour tenir compte des petites fluctuations des performances du réseau, le initialBitrate recommandé renvoyé par cette méthode est légèrement inférieur au débit binaire réel mesuré au cours du test. (Il est généralement déconseillé d’utiliser 100 % de la bande passante disponible.)

func runBroadcastTest() { self.test = session.recommendedVideoSettings(with: IVS_RTMPS_URL, streamKey: IVS_STREAMKEY) { [weak self] result in if result.status == .success { self?.recommendation = result.recommendations[0]; } } }

Utilisation de la reconnexion automatique

IVS prend en charge la reconnexion automatique à une diffusion si celle-ci s’arrête de manière inattendue sans appeler l’API stop ; par exemple, une perte temporaire de connectivité réseau. Pour activer la reconnexion automatique, définissez la propriété enabled sur IVSBroadcastConfiguration.autoReconnect à true.

Lorsque quelque chose provoque l’arrêt inattendu du flux, le kit SDK réessaie jusqu’à cinq fois, en suivant une stratégie de backoff linéaire. Elle notifie votre application de l’état de réessai via la fonction IVSBroadcastSessionDelegate.didChangeRetryState.

La reconnexion automatique utilise pour cela la fonctionnalité de reprise de flux de l’IVS en ajoutant un numéro de priorité, commençant par 1, à la fin de la clé de flux fournie. Pendant la durée de l’instance IVSBroadcastSession, ce numéro est incrémenté de 1 à chaque tentative de reconnexion. Cela signifie que si la connexion de l’appareil est perdue quatre fois au cours d’une diffusion, et que chaque perte nécessite de 1 à 4 tentatives, la priorité du dernier flux remonté peut être comprise entre 5 et 17. Pour cette raison, nous vous recommandons de ne pas utiliser la reprise de flux IVS à partir d’un autre appareil lorsque la reconnexion automatique est activée dans le kit SDK pour le même canal. Il n’y a aucune garantie quant à la priorité utilisée par le kit SDK à ce moment-là, et le kit SDK essaiera de se reconnecter avec une priorité plus élevée si un autre appareil prend le relais.

Utiliser la vidéo en arrière-plan

Vous pouvez continuer une diffusion non-RelayKit, même avec votre application en arrière-plan.

Afin d’économiser de l’énergie et de conserver la réactivité des applications de premier plan, iOS ne donne accès au GPU qu’à une seule application à la fois. Le kit SDK de diffusion Amazon IVS utilise le GPU à plusieurs étapes du pipeline vidéo, y compris la composition de plusieurs sources d’entrée, l’ajustement de l’image et l’encodage de l’image. Bien que l’application de diffusion soit en arrière-plan, rien ne garantit que le SDK puisse effectuer l’une ou l’autre de ces actions.

Pour y remédier, utilisez la méthode createAppBackgroundImageSource. Il permet au kit SDK de continuer à diffuser de la vidéo et de l’audio en arrière-plan. Il renvoie un IVSBackgroundImageSource, qui est normal IVSCustomImageSource avec une fonction finish supplémentaire. Tous CMSampleBuffer ce qui est fourni à la source de l’image d’arrière-plan est encodé à la fréquence d’images fournie par votreIVSVideoConfiguration original. Les horodatages sur le CMSampleBuffer sont ignorés.

Le kit SDK redimensionne et encode ensuite ces images puis les met en cache, ce qui permet de boucler automatiquement ce flux lorsque votre application passe en arrière-plan. Lorsque votre application revient au premier plan, les périphériques d’image connectés redeviennent actifs et le flux pré-encodé cesse de tourner en boucle.

Pour annuler ce processus, utilisez removeImageSourceOnAppBackgrounded. Vous n’avez pas besoin d’appeler cette fonction à moins que vous ne souhaitiez rétablir explicitement le mode en arrière-plan du SDK. Autrement, il est automatiquement nettoyé lors de la désallocation du IVSBroadcastSession.

Remarques: Nous vous recommandons fortement de recourir à cette méthode dans le cadre de la configuration de la séance de diffusion, avant la mise en service de la session. La méthode est coûteuse (elle encode la vidéo), de sorte que les performances d’une diffusion en direct pendant l’exécution de cette méthode peuvent être dégradées.

Exemple : Générer une image statique pour la vidéo d’arrière-plan

Fournir une seule image à la source d’arrière-plan génère un GOP complet de cette image statique.

Voici un exemple utilisant CIImage :

// Create the background image source guard let source = session.createAppBackgroundImageSource(withAttemptTrim: true, onComplete: { error in print("Background Video Generation Done - Error: \(error.debugDescription)") }) else { return } // Create a CIImage of the color red. let ciImage = CIImage(color: .red) // Convert the CIImage to a CVPixelBuffer let attrs = [ kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue, kCVPixelBufferMetalCompatibilityKey: kCFBooleanTrue, ] as CFDictionary var pixelBuffer: CVPixelBuffer! CVPixelBufferCreate(kCFAllocatorDefault, videoConfig.width, videoConfig.height, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, attrs, &pixelBuffer) let context = CIContext() context.render(ciImage, to: pixelBuffer) // Submit to CVPixelBuffer and finish the source source.add(pixelBuffer) source.finish()

Au lieu de créer une CIImage d’une couleur solide, vous pouvez aussi utiliser des images groupées. Le seul code affiché ici est celui de la conversion d’une UIImage en CiImage à utiliser avec l’échantillon précédent :

// Load the pre-bundled image and get it’s CGImage guard let cgImage = UIImage(named: "image")?.cgImage else { return } // Create a CIImage from the CGImage let ciImage = CIImage(cgImage: cgImage)

Exemple : Vidéo avec AvassetImageGenerator

Vous pouvez utiliser un AVAssetImageGenerator pour générer CMSampleBuffers à partir de AVAsset (mais pas un flux HLS)AVAsset) :

// Create the background image source guard let source = session.createAppBackgroundImageSource(withAttemptTrim: true, onComplete: { error in print("Background Video Generation Done - Error: \(error.debugDescription)") }) else { return } // Find the URL for the pre-bundled MP4 file guard let url = Bundle.main.url(forResource: "sample-clip", withExtension: "mp4") else { return } // Create an image generator from an asset created from the URL. let generator = AVAssetImageGenerator(asset: AVAsset(url: url)) // It is important to specify a very small time tolerance. generator.requestedTimeToleranceAfter = .zero generator.requestedTimeToleranceBefore = .zero // At 30 fps, this will generate 4 seconds worth of samples. let times: [NSValue] = (0...120).map { NSValue(time: CMTime(value: $0, timescale: CMTimeScale(config.video.targetFramerate))) } var completed = 0 let context = CIContext(options: [.workingColorSpace: NSNull()]) // Create a pixel buffer pool to efficiently feed the source let attrs = [ kCVPixelBufferPixelFormatTypeKey: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue, kCVPixelBufferMetalCompatibilityKey: kCFBooleanTrue, kCVPixelBufferWidthKey: videoConfig.width, kCVPixelBufferHeightKey: videoConfig.height, ] as CFDictionary var pool: CVPixelBufferPool! CVPixelBufferPoolCreate(kCFAllocatorDefault, nil, attrs, &pool) generator.generateCGImagesAsynchronously(forTimes: times) { requestTime, image, actualTime, result, error in if let image = image { // convert to CIImage then CVpixelBuffer let ciImage = CIImage(cgImage: image) var pixelBuffer: CVPixelBuffer! CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool, &pixelBuffer) context.render(ciImage, to: pixelBuffer) source.add(pixelBuffer) } completed += 1 if completed == times.count { // Mark the source finished when all images have been processed source.finish() } }

Il est possible de générer CVPixelBuffers en utilisant un AVPlayer et AVPlayerItemVideoOutput. Toutefois, cela nécessite l’utilisation d’un CADisplayLink et s’exécute plus près en temps réel, tandis que AVAssetImageGenerator peut traiter les images beaucoup plus rapidement.

Limites

Votre application a besoin du droit à l’audio en arrière-plan pour éviter d’être suspendu après être passée en arrière-plan.

createAppBackgroundImageSource ne peut être appelé que lorsque votre application est au premier plan, car elle a besoin d’accéder au GPU pour compléter.

createAppBackgroundImageSource encode toujours un GOP complet. Par exemple, si vous disposez d’un intervalle d’image-clé de 2 secondes (valeur par défaut) et que vous exécutez 30 f/s, il encode un multiple de 60 images.

  • Si moins de 60 images sont fournies, la dernière image est répétée jusqu’à ce que 60 images soient atteintes, quelle que soit la valeur de l’option trim.

  • Si plus de 60 images sont fournis et que l’option de trim est true, les dernières images N sont supprimées, N étant le reste du nombre total d’images soumises divisé par 60.

  • Si plus de 60 images sont fournis et que l’option de trim est false, la dernière image est répétée jusqu’à ce que le prochain multiple de 60 images soit atteint.