

# Fortgeschrittene Anwendungsfälle für das IVS-iOS-Broadcast-SDK \| Streaming mit niedriger Latenz
<a name="broadcast-ios-use-cases"></a>

Hier stellen wir einige fortschrittliche Anwendungsfälle vor. Beginnen Sie mit dem obigen Basis-Setup und fahren Sie hier fort.

## Broadcast-Konfiguration erstellen
<a name="broadcast-ios-create-configuration"></a>

Hier erstellen wir eine benutzerdefinierte Konfiguration mit zwei Mischersteckplätzen, die es uns erlauben, zwei Videoquellen an den Mischer zu binden. Eine (`custom`) ist Vollbild und hinter der anderen angelegt (`camera`), die kleiner und in der unteren rechten Ecke ist. Beachten Sie, dass wir für den `custom`-Slot keine Position, Größe oder Seitenverhältnis festlegen. Da wir diese Parameter nicht einstellen, verwendet der Slot die Videoeinstellungen für Größe und 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
    }()
]
```

## Erstellen der Broadcast-Sitzung (Advanced-Version)
<a name="broadcast-ios-create-session-advanced"></a>

Erstellen Sie eine `IVSBroadcastSession` wie im [grundlegenden Beispiel](broadcast-ios-getting-started.md#broadcast-ios-create-session), geben Sie jedoch hier Ihre benutzerdefinierte Konfiguration an. Geben Sie auch `nil` für das Gerätearray an, da wir diese manuell hinzufügen werden.

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

## Iterieren und Anschließen eines Kamerageräts
<a name="broadcast-ios-attach-camera"></a>

Hier iterieren wir durch Eingabegeräte, die das SDK erkannt hat. Das SDK gibt nur integrierte Geräte auf iOS zurück. Selbst wenn Bluetooth-Audiogeräte angeschlossen sind, werden sie als integriertes Gerät angezeigt. Weitere Informationen finden Sie unter [Bekannte Probleme und Problemumgehungen im IVS-iOS-Broadcast-SDK \| Streaming mit niedriger Latenz](broadcast-ios-issues.md).

Sobald wir ein Gerät gefunden haben, das wir verwenden möchten, rufen wir `attachDevice` auf, um es anzuhängen:

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

## Kameras austauschen
<a name="broadcast-ios-swap-cameras"></a>

```
// 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)")
        }
    }
}
```

## Erstellen einer benutzerdefinierten Eingabequelle
<a name="broadcast-ios-create-input-source"></a>

Um Audio- oder Image-Daten einzugeben, die von Ihrer App generiert werden, verwenden Sie `createImageSource` oder `createAudioSource`. Beide Methoden erstellen virtuelle Geräte (`IVSCustomImageSource` and `IVSCustomAudioSource`), die wie jedes andere Gerät an den Mischer gebunden werden können.

Die Geräte, die von beiden diesen Methoden zurückgegeben werden, akzeptieren `CMSampleBuffer` durch die `onSampleBuffer`-Funktion:
+ Bei Videoquellen muss das Pixelformat `kCVPixelFormatType_32BGRA`, `420YpCbCr8BiPlanarFullRange`, oder `420YpCbCr8BiPlanarVideoRange` sein.
+ Bei Audioquellen muss der Puffer lineare PCM-Daten enthalten.

Sie können ein `AVCaptureSession` mit Kameraeingang nicht verwenden, um eine benutzerdefinierte Image-Quelle zu füllen, während Sie gleichzeitig ein Kameragerät verwenden, das vom Broadcast-SDK bereitgestellt wird. Wenn Sie mehrere Kameras gleichzeitig verwenden möchten, verwenden Sie `AVCaptureMultiCamSession` und stellen Sie zwei benutzerdefinierte Image-Quellen bereit.

Benutzerdefinierte Image-Quellen sollten in erster Linie mit statischen Inhalten wie Bildern oder mit Videoinhalten verwendet werden:

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

## Überwachen der Netzwerkverbindung
<a name="broadcast-ios-network-connection"></a>

Es ist üblich, dass mobile Geräte vorübergehend verlieren und die Netzwerkverbindung wiedererlangen, während sie unterwegs sind. Aus diesem Grund ist es wichtig, die Netzwerkkonnektivität Ihrer App zu überwachen und entsprechend zu reagieren, wenn sich Dinge ändern. 

Wenn die Verbindung des Broadcasters unterbrochen wird, ändert sich der Status des Broadcast-SDK in `error` und dann `disconnected`. Sie werden über diese Änderungen über die benachrichtigt `IVSBroadcastSessionDelegate`. Wenn Sie diese Statusänderungen erhalten:

1. Überwachen Sie den Verbindungsstatus Ihrer Broadcast-App und rufen Sie `start` mit Ihrem Endpunkt und Streamschlüssel auf, sobald Ihre Verbindung wiederhergestellt wurde.

1. **Wichtig:** Überwachen Sie den Rückruf des Zustandsdelegaten und stellen Sie sicher, dass sich der Status nach dem Aufruf `start` zu `connected` ändert.

## Trennen eines Geräts
<a name="broadcast-ios-detach-device"></a>

Wenn Sie ein Gerät trennen und nicht ersetzen möchten, trennen Sie es mit `IVSDevice` oder `IVSDeviceDescriptor`:

```
broadcastSession.detachDevice(currentCamera)
```

## ReplayKit-Integration
<a name="broadcast-ios-replaykit"></a>

Um den Bildschirm und das Systemaudio des Geräts auf iOS zu streamen, müssen Sie [ReplayKit](https://developer.apple.com/documentation/replaykit?language=objc) integrieren. Das Amazon IVS-Broadcast-SDK erleichtert die Integration von ReplayKit mit `IVSReplayKitBroadcastSession`. In Ihrer `RPBroadcastSampleHandler`-Unterklasse erstellen Sie eine Instance von `IVSReplayKitBroadcastSession`, dann:
+ Starten Sie die Sitzung in `broadcastStarted`
+ Beenden Sie die Sitzung in `broadcastFinished`

Das Sitzungsobjekt verfügt über drei benutzerdefinierte Quellen für Bildschirm-Images, App-Audio und Mikrofonaudio. Übergeben Sie das in `processSampleBuffer` angegebene `CMSampleBuffers` an diese benutzerdefinierten Quellen.

Um die Geräteausrichtung zu verarbeiten, müssen Sie Replaykit-spezifische Metadaten aus dem Beispielpuffer extrahieren. Verwenden Sie folgenden Code:

```
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))
    }
}
```

Es ist möglich, ReplayKit mit `IVSBroadcastSession` anstelle von `IVSReplayKitBroadcastSession` zu integrieren. Die ReplayKit-spezifische Variante weist jedoch mehrere Modifikationen auf, um den internen Speicherbedarf zu reduzieren, um innerhalb der Speicherobergrenze von Apple für Broadcast-Erweiterungen zu bleiben.

## Empfohlene Broadcast-Einstellungen erhalten
<a name="broadcast-ios-recommended-settings"></a>

Um die Verbindung Ihres Benutzers vor dem Starten einer Übertragung zu bewerten, verwenden Sie `IVSBroadcastSession.recommendedVideoSettings`, um einen kurzen Test durchzuführen. Während der Testläufe erhalten Sie mehrere Empfehlungen, geordnet von den am meisten empfohlenen zu den am wenigsten empfohlenen. In dieser Version des SDK ist es nicht möglich, die aktuelle `IVSBroadcastSession` neu zu konfigurieren, daher müssen Sie sie freigeben und dann eine neue mit den empfohlenen Einstellungen erstellen. Sie erhalten weiterhin `IVSBroadcastSessionTestResults` bis `result.status` `Success` ist oder `Error`. Sie können den Fortschritt mit überprüfen `result.progress`.

Amazon IVS unterstützt eine maximale Bitrate von 8,5 Mbit/s (für Kanäle, deren `type` `STANDARD` oder `ADVANCED` ist), so dass die `maximumBitrate`, die von dieser Methode zurückgegeben wird, nie 8,5 Mbps überschreitet. Um kleine Schwankungen der Netzwerkleistung zu berücksichtigen, ist die von dieser Methode empfohlene `initialBitrate` etwas niedriger als die im Test gemessene tatsächliche Bitrate. (Die Verwendung von 100 % der verfügbaren Bandbreite ist in der Regel nicht ratsam.)

```
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];
        }
    }
}
```

## Verwenden der automatischen Wiederverbindung
<a name="broadcast-ios-auto-reconnect"></a>

IVS unterstützt die automatische Wiederverbindung mit einem Broadcast, falls der Broadcast unerwartet beendet wird, ohne die `stop`-API aufzurufen, z. B. bei einem vorübergehenden Verlust der Netzwerkkonnektivität. Legen Sie zum Aktivieren der automatischen Wiederverbindung die Eigenschaft `enabled` für `IVSBroadcastConfiguration.autoReconnect` auf `true` fest.

Wenn der Stream aus irgendeinem Grund unerwartet beendet wird, versucht das SDK die Wiederverbindung bis zu fünf Mal. Dabei folgt es einer linearen Backoff-Strategie. Es benachrichtigt Ihre Anwendung mithilfe der Funktion `IVSBroadcastSessionDelegate.didChangeRetryState` über den Status der erneuten Versuche.

Die automatische Wiederverbindung nutzt im Hintergrund die IVS-Funktion für die [Stream-Übernahme](streaming-config.md#streaming-config-stream-takeover), indem eine Prioritätsnummer, beginnend mit 1, an das Ende des bereitgestellten Stream-Schlüssels angehängt wird. Für die Dauer der `IVSBroadcastSession`-Instance wird diese Zahl bei jedem erneuten Verbindungsversuch um 1 erhöht. Das bedeutet Folgendes: Wenn die Verbindung des Geräts während eines Broadcasts viermal unterbrochen wird und für jede Unterbrechung 1–4 Wiederholungsversuche erforderlich sind, kann die Priorität des letzten Streamups zwischen 5 und 17 liegen. Aus diesem Grund *empfehlen wir, die IVS-Stream-Übernahme von einem anderen Gerät nicht zu verwenden, solange die automatische Wiederverbindung im SDK für denselben Kanal aktiviert ist*. Es gibt keine Garantie dafür, welche Priorität das SDK zu diesem Zeitpunkt verwendet, und das SDK versucht, die Verbindung mit einer höheren Priorität wiederherzustellen, wenn ein anderes Gerät den Stream übernimmt.

## Hintergrundvideo verwenden
<a name="broadcast-ios-background-video"></a>

Sie können eine Nicht-Relaykit-Sendung fortsetzen, auch wenn Ihre Anwendung im Hintergrund liegt.

Um Strom zu sparen und Vordergrundanwendungen aktiv zu halten, gewährt iOS jeweils nur einer Anwendung Zugriff auf die GPU. Das Amazon IVS-Broadcast-SDK verwendet die GPU in mehreren Stages der Videopipeline, einschließlich der Zusammenstellung mehrerer Eingangsquellen, der Skalierung und der Kodierung des Images. Während sich die Sendeanwendung im Hintergrund befindet, gibt es keine Garantie dafür, dass das SDK eine dieser Aktionen ausführen kann.

Verwenden Sie dazu die `createAppBackgroundImageSource`-Methode. So kann das SDK im Hintergrund weiterhin sowohl Video als auch Audio übertragen. Es gibt eine `IVSBackgroundImageSource` als normale `IVSCustomImageSource` mit einer zusätzlichen `finish`-Funktion zurück. Jede zur Hintergrundbildquelle zur Verfügung gestellte `CMSampleBuffer` wird mit der Image-Rate codiert, die von Ihrer Original-`IVSVideoConfiguration` bereitgestellt wird. Zeitstempel auf der `CMSampleBuffer` werden ignoriert.

Das SDK skaliert und codiert diese Bilder dann und speichert sie im Cache, wobei dieser Feed automatisch durchläuft, wenn Ihre Anwendung in den Hintergrund geht. Wenn Ihre Anwendung in den Vordergrund zurückkehrt, werden die angeschlossenen Image-Geräte wieder aktiv und die Schleife des vorkodierten Streams stoppt.

Um diesen Vorgang rückgängig zu machen, verwenden Sie `removeImageSourceOnAppBackgrounded`. Sie müssen dies nicht aufrufen, es sei denn, Sie möchten das Hintergrundverhalten des SDK explizit rückgängig machen, andernfalls wird es bei Beendigung der automatisch bereinigt `IVSBroadcastSession`.

**Hinweise:** *Wir empfehlen dringend, diese Methode im Rahmen der Konfiguration der Broadcast-Sitzung aufzurufen, bevor die Sitzung live geht.* Die Methode ist teuer (sie kodiert Video), daher kann die Leistung einer Live-Übertragung während der Ausführung dieser Methode beeinträchtigt werden.

### Beispiel: Generieren eines statischen Images für Hintergrundvideo
<a name="background-video-example-static-image"></a>

Wenn Sie der Hintergrundquelle ein einzelnes Image bereitstellen, wird eine vollständige GOP dieses statischen Images generiert.

Hier finden Sie ein Beispiel mit 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()
```

Anstatt ein CiImage in einer Volltonfarbe zu erstellen, können Sie alternativ gebündelte Bilder verwenden. Der einzige hier gezeigte Code ist zur Konvertierung eines UIImage in ein CiImage, um es mit dem vorherigen Beispiel zu verwenden:

```
// 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)
```

### Beispiel: Video mit AvasseTimageGenerator
<a name="background-video-example-avassetimagegenerator"></a>

Sie können einen `AVAssetImageGenerator` verwenden, um `CMSampleBuffers` aus einem `AVAsset` zu generieren (obwohl kein HLS-Stream-`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()
    }
}
```

Es kann ein `CVPixelBuffers` durch Nutzung eines `AVPlayer` und `AVPlayerItemVideoOutput` erstellt werden. Dies erfordert jedoch die Verwendung eines `CADisplayLink` und kommt näher an Echtzeit heran, während `AVAssetImageGenerator` die Frames viel schneller verarbeiten kann.

### Einschränkungen
<a name="background-video-limitations"></a>

Ihre Anwendung benötigt [Hintergrund-Audio-Berechtigung](https://developer.apple.com/documentation/xcode/configuring-background-execution-modes), um zu vermeiden, dass sie nach dem Gang in den Hintergrund suspendiert wird.

`createAppBackgroundImageSource` kann nur aufgerufen werden, solange Ihre Anwendung im Vordergrund ist, da sie zum Abschluss Zugriff auf die GPU benötigt.

`createAppBackgroundImageSource` kodiert immer zu einer vollständigen GOP. Wenn Sie beispielsweise ein Keyframe-Intervall von 2 Sekunden haben (Standardeinstellung) und mit 30 fps oeprieren, wird ein Vielfaches von 60 Frames kodiert.
+ Wenn weniger als 60 Frames bereitgestellt werden, wird unabhängig vom Wert der Trimmoption das letzte Frame wiederholt, bis 60 Frames erreicht sind.
+ Wenn mehr als 60 Frames vorhanden sind und die Trimmoption `true` gewählt ist, werden die letzten n Frames gelöscht, wobei n der Rest der Gesamtzahl der übermittelten Frames geteilt durch 60 ist.
+ Wenn mehr als 60 Frames vorhanden sind und die Trimmoption `false` gewählt ist, wird das letzte Frame wiederholt, bis das nächste Vielfache von 60 Frames erreicht ist.