iOS-Codebeispiel für die Bildschirmfreigabe

Übersicht

In dieser Anleitung wird gezeigt, wie Sie mit dem iOS-SDK ein Video zur Bildschirmfreigabe veröffentlichen können, wobei der Bildschirm des Geräts als Quelle für das Video des Streams dient.

Ihr Projekt einrichten

Der Code für diesen Abschnitt befindet sich in der Bildschirmfreigabe Zweigstelle der lern-opentok-ios repoWenn Sie es also noch nicht getan haben, müssen Sie das Repo in ein lokales Verzeichnis klonen - Dies kann über die Befehlszeile erfolgen:

git clone https://github.com/opentok/learning-opentok-ios.git

Dann schauen Sie sich die Filiale an:

git checkout screen-sharing

Dieser Zweig zeigt Ihnen, wie Sie den Bildschirm (ein UIView) mit einem benutzerdefinierten Video-Capturer aufnehmen. Öffnen Sie das Projekt in XCode, um zu folgen.

Das ist wichtig: Bitte beachten Sie, dass Ihr App-ID ist Ihr API-Schlüssel.

Erforschung des Codes

Dieses Beispiel verwendet die initCapture, releaseCapture, startCapture, stopCaptureund isCaptureStarted Methoden der Klasse OTVideoKit, um die Aufnahmefunktionen der Anwendung zu verwalten.

Die ViewController-Klasse erstellt eine Sitzung, instanziiert Abonnenten und richtet den Herausgeber ein.

Die Klasse OTKBasicVideoCapturer erstellt einen Frame, nimmt einen Screenshot auf, versieht den Frame mit einem Zeitstempel und speichert ihn in einer Instanz von consumer.

Der Herausgeber greift auf den Verbraucher zu, um den Rahmen zu erhalten.

Die initCapture Methode wird zur Initialisierung der Erfassung verwendet und legt den Wert für das Pixelformat eines OTVideoFrame-Objekts fest. In diesem Beispiel wird der Wert auf ARGB gesetzt.

- (void)initCapture
{
    self.format = [[OTVideoFormat alloc] init];
    self.format.pixelFormat = OTPixelFormatARGB;
}

Die releaseCapture Methode löscht den Speicherpuffer:

- (void)releaseCapture
{
    self.format = nil;
}

Die startCapture Methode erzeugt einen eigenen Thread und ruft die produceFrame Methode, um Bildschirmaufnahmen zu starten:

- (int32_t)startCapture
{
    self.captureStarted = YES;
    dispatch_after(kTimerInterval,
                    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),
                    ^{
                        @autoreleasepool {
                            [self produceFrame];
                        }
                    });

    return 0;
}

Die produceFrame Methode:

  • Definiert den Rahmen für aufgenommene Bilder
  • Erzeugt einen Zeitstempel zur Kennzeichnung eines aufgenommenen Bildes
  • Nimmt einen Screenshot auf
  • Konvertiert das Bildschirmfoto in ein lesbares Format
  • Markiert den Screenshot mit einem Zeitstempel
  • Berechnet die Größe des Bildes
  • Setzt den consumeFrame mit dem Bild
  • Ruft sich selbst 15 Mal pro Sekunde auf, sobald die Erfassung beginnt

Der Rahmen für die erfassten Bilder wird als ein Objekt von OTVideoFrame festgelegt. Die Eigenschaften von OTVideoFrame definieren die Ebenen, den Zeitstempel, die Ausrichtung und das Format eines Bildes.

OTVideoFrame *frame = [[OTVideoFrame alloc] initWithFormat:self.format];

Zur Kennzeichnung des Bildes wird ein Zeitstempel erstellt. Jedes Bild wird mit einem Zeitstempel versehen, so dass Herausgeber und Abonnent in der Lage sind, die gleiche Zeitleiste zu erstellen und die Bilder in der gleichen Reihenfolge zu referenzieren.

static mach_timebase_info_data_t time_info;
uint64_t time_stamp = 0;

time_stamp = mach_absolute_time();
time_stamp *= time_info.numer;
time_stamp /= time_info.denom;

Die Methode screenshot wird aufgerufen, um ein Bild des Bildschirms zu erhalten.

CGImageRef screenshot = [[self screenshot] CGImage];

Die Methode fillPixelBufferFromCGImage wandelt die Bilddaten eines CGImage in einen CVPixelBuffer um.

[self fillPixelBufferFromCGImage:screenshot];

Das Bild wird mit einem Zeitstempel versehen und die Erfassungsrate in Bildern pro Sekunde sowie die Verzögerung zwischen den Aufnahmen werden festgelegt.

CMTime time = CMTimeMake(time_stamp, 1000);
frame.timestamp = time;
frame.format.estimatedFramesPerSecond = kFramesPerSecond;
frame.format.estimatedCaptureDelay = 100;

Die Anzahl der Bytes in einer einzelnen Zeile wird mit der Höhe des Bildes multipliziert, um die Größe des Bildes zu erhalten. Hinweis: Das Einzelelement-Array und die Bytes pro Zeile basieren auf einer 4-Byte-Spezifikation eines RGB-Bildes auf einer einzigen Ebene.

frame.format.imageWidth = CVPixelBufferGetWidth(pixelBuffer);
frame.format.imageHeight = CVPixelBufferGetHeight(pixelBuffer);
frame.format.bytesPerRow = [@[@(frame.format.imageWidth * 4)] mutableCopy];
frame.orientation = OTVideoOrientationUp;

CVPixelBufferLockBaseAddress(pixelBuffer, 0);
uint8_t *planes[1];

planes[0] = CVPixelBufferGetBaseAddress(pixelBuffer);
[frame setPlanesWithPointers:planes numPlanes:1];

Das Bild wird in einer Instanz von consumer gespeichert. Der Herausgeber greift auf die aufgenommenen Bilder über die Verbraucherinstanz zu.

[self.consumer consumeFrame:frame];

Der Pixelpuffer wird geleert und eine Warteschlange mit Hintergrundpriorität (getrennt von der von der Benutzeroberfläche verwendeten Warteschlange) wird für die Bilderfassung verwendet. Wenn die Bildaufnahme im Gange ist, wird die produceFrame Methode ruft sich selbst 15 Mal pro Sekunde auf.

    CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
    if (self.captureStarted) {
        dispatch_after(kTimerInterval,
                        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),
                        ^{
                            @autoreleasepool {
                                [self produceFrame];
                            }
                        });
    }

Die screenshot Methode nimmt ein Bildschirmfoto auf und gibt ein Bild zurück. Diese Methode wird aufgerufen von der produceFrame Methode.

- (UIImage *)screenshot
{
    CGSize imageSize = CGSizeZero;

    imageSize = [UIScreen mainScreen].bounds.size;

    UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
    UIWindow *window = [UIApplication sharedApplication].keyWindow;

    if ([window respondsToSelector:
            @selector(drawViewHierarchyInRect:afterScreenUpdates:)])
    {
        [window drawViewHierarchyInRect:window.bounds afterScreenUpdates:NO];
    }
    else {
        [window.layer renderInContext:UIGraphicsGetCurrentContext()];
    }

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}