画面共有のためのiOSコード例

概要

このガイドでは、iOS SDKを使用して、デバイスの画面をストリームのビデオのソースとして使用し、画面共有ビデオを公開する方法を示します。

プロジェクトの設定

このセクションのコードは 画面共有 の支部である。 learning-opentok-iosレポもしまだなら、レポをローカル・ディレクトリにクローンする必要がある。 これはコマンドラインを使って実行できる:

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

そして支店をチェックする:

git checkout screen-sharing

このブランチでは、カスタムビデオキャプチャを使って画面(UIView)をキャプチャする方法を紹介します。XCodeでプロジェクトを開いて、一緒に見ていきましょう。

重要だ: あなたの アプリID はあなたの APIキー.

コードを探る

このサンプルでは initCapture, releaseCapture, startCapture, stopCaptureそして isCaptureStarted OTVideoKitクラスのメソッドを使用して、アプリケーションのキャプチャ機能を管理します。

ViewController クラスはセッションを作成し、 サブスクライバをインスタンス化し、パブリッシャを設定します。

OTKBasicVideoCapturerクラスは、フレームを作成し、スクリーンショットをキャプチャし、フレームにタイムスタンプを付けてconsumerのインスタンスに保存します。

パブリッシャーはフレームを取得するためにコンシューマーにアクセスする。

について initCapture メソッドは、キャプチャの初期化に使用され、OTVideoFrameオブジェクトのピクセル・フォーマットの値を設定する。この例では、ARGBに設定されています。

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

について releaseCapture メソッドはメモリ・バッファをクリアする:

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

について startCapture メソッドは別のスレッドを作成し produceFrame メソッドを使ってスクリーンキャプチャを開始する:

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

    return 0;
}

について produceFrame メソッドを使用する:

  • キャプチャ画像のフレームを定義する
  • キャプチャした画像にタグを付けるためのタイムスタンプを作成します。
  • スクリーンショットを撮る
  • スクリーンショットを読みやすい形式に変換します。
  • スクリーンショットにタイムスタンプを付ける
  • 画像のサイズを計算する
  • consumeFrameに画像を設定する。
  • キャプチャ開始後、毎秒15回呼び出す

キャプチャされた画像のフレームは、OTVideoFrame オブジェクトとして設定される。OTVideoFrameのプロパティは、フレームのプレーン、タイムスタンプ、向き、フォーマットを定義する。

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

画像にタグを付けるためにタイムスタンプが作成されます。すべての画像にタイムスタンプが付けられるので、パブリッシャーとサブスクライバーの両方が同じタイムラインを作成し、同じ順番でフレームを参照することができます。

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;

スクリーンショット・メソッドは、画面の画像を取得するために呼び出される。

CGImageRef screenshot = [[self screenshot] CGImage];

fillPixelBufferFromCGImage メソッドは、CGImage の画像データを CVPixelBuffer に変換します。

[self fillPixelBufferFromCGImage:screenshot];

フレームにはタイムスタンプが付けられ、キャプチャレート(フレーム/秒)とキャプチャ間のディレイが設定される。

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

1行のバイト数に画像の高さを掛け合わせたものが画像のサイズとなる。なお、単一要素の配列と行ごとのバイト数は、RGB画像の4バイト単一プレーン指定に基づいています。

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

フレームはコンシューマーのインスタンスに保存される。パブリッシャーはコンシューマーのインスタンスを通してキャプチャした画像にアクセスします。

[self.consumer consumeFrame:frame];

ピクセル・バッファはクリアされ、(UIが使用するキューとは別の)バックグラウンド優先のキューが画像キャプチャに使用される。画像キャプチャが進行中の場合は produceFrame メソッドは1秒間に15回自分自身を呼び出す。

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

について screenshot メソッドはスクリーンショットを受け取り、画像を返します。このメソッドは produceFrame メソッドを使用する。

- (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;
}