iOS / iPadOS 向けガイド

概要

iOS / iPadOS アプリで sora_sdk を利用する方法について説明します。

権限

iOS / iPadOS で音声や映像を配信する場合はカメラとマイクの権限が必要になります。SDK は権限リクエストを行わないため、アプリ側で Info.plist の用途説明の設定とランタイム権限のリクエストを実装してください。

Info.plist の用途説明

<key>NSCameraUsageDescription</key>
<string>カメラを利用する目的を記述します</string>
<key>NSMicrophoneUsageDescription</key>
<string>マイクを利用する目的を記述します</string>

ランタイム権限のリクエスト

Dart 側の実装例として permission_handler パッケージを使うと、次のように権限を確保できます。

import 'package:permission_handler/permission_handler.dart';

// 必要な権限のみユーザーにリクエストする
Future<bool> ensureMediaPermissions({
  required bool needsCamera,
  required bool needsMicrophone,
}) async {
  // 配信内容に応じて要求する権限を組み立てる
  final permissions = <Permission>[
    if (needsCamera) Permission.camera,
    if (needsMicrophone) Permission.microphone,
  ];
  // 要求する権限がなければそのまま許可扱い
  if (permissions.isEmpty) {
    return true;
  }
  // ダイアログを表示し、すべての権限が許可されたかを返す
  final statuses = await permissions.request();
  return statuses.values.every((status) => status.isGranted);
}

接続前に次のように呼び出します。

// カメラとマイクの権限をリクエストする
final granted = await ensureMediaPermissions(
  needsCamera: true,
  needsMicrophone: true,
);
// いずれかの権限が拒否された場合は接続せずに終了する
if (!granted) {
  return;
}
// 権限が揃ってからメディアを取得する
final stream = await MediaDevices.getUserMedia(
  const GetUserMediaOptions(audio: true, video: true),
);
await client.connect(stream);

MethodChannel とネイティブ実装を組み合わせて権限を扱う場合は、 sora_sdk/devtools/ios/Runner/AppDelegate.swiftsora_sdk/devtools/lib/main.dart_ensureMediaPermissions を参考にしてください。

映像レンダリング

iOS / iPadOS でも映像レンダリングは Flutter の Texture を使います。取得元はローカル映像とリモート映像の 2 種類があり、それぞれ別の経路で textureId を受け取ります。

ローカル映像のレンダリング

ローカル映像は、 MediaDevices.getUserMedia() で取得した LocalVideoTrack から textureId を取り出して Texture に渡すことでレンダリングできます。接続前のカメラプレビューもこの方法で行えます。

// カメラ・マイクから LocalMediaStream を取得する
final stream = await MediaDevices.getUserMedia(
  const GetUserMediaOptions(audio: true, video: true),
);

// 映像トラックから textureId を取得する
final videoTrack = stream.getVideoTracks().first;
final localTextureId = await videoTrack.textureId;
// 取得した textureId を Flutter の Texture に渡してレンダリングする
Texture(textureId: localTextureId)

接続は任意のタイミングで行います。

// Sora に接続して配信を開始する
await client.connect(stream);

リモート映像のレンダリング

接続相手から受信した映像は client.eventsSoraTrackEvent で通知されます。 track.kind'video' のとき、 track.textureIdTexture に渡してレンダリングします。

// 接続相手の映像トラック追加イベントを受け取る
client.events.listen((event) {
  // 映像トラックのときだけ textureId を保持する
  if (event case SoraTrackEvent(:final track) when track.kind == 'video') {
    setState(() {
      remoteTextureId = track.textureId;
    });
  }
});
// 受け取った textureId を Flutter の Texture に渡してレンダリングする
Texture(textureId: remoteTextureId!)

シミュレーターに関する注意

  • iOS シミュレーターではカメラデバイスが利用できません
  • そのため sendonly / sendrecv の映像送信確認には不向きです
  • recvonly / sendrecv の映像受信は動作しますが、実機より性能が低い場合があります
  • マイク権限や音声ルーティングの挙動は実機と一致しない場合があります

ビルド時の注意

  • 動作確認の開始時は flutter build ios --simulator --debugflutter run を使うと切り分けしやすくなります
  • 実機確認や配布ビルドでは flutter build ios --release を前提に確認してください
  • iOS と iPadOS は実機とシミュレーターで挙動差があるため、送信系の最終確認は実機で行ってください
  • カメラやマイクを使う場合は、ビルド前に Info.plist の用途説明を必ず設定してください
  • アプリ側で CocoaPods や Xcode 設定を独自に変更する場合は、SDK 側が前提にしているリンク設定を壊さないように注意してください
  • シミュレーターのアーキテクチャ条件やビルド条件を大きく変更すると、ビルドや起動に失敗する場合があります