ライブ配信 (Android)

ここでは Castify SDK を使って簡単なライブ配信機能を備えた画面を実装する流れを説明します。

このチュートリアルでは Jetpack Compose の利用を前提としていますが、従来の Activity を利用することも可能です。

配信機能を備えた Composable を作成する

以降の説明は、以下のような空の Composable からスタートするものとします。完全なソースコードはこちらopen in new windowで閲覧できます。

@Composable
fun LiveScreen() { /* TODO */ }

1. カメラ/マイクへのアクセスを行うためのパーミッションを取得する

このサンプルではカメラとマイクへのアクセスを行うため accompanist-permissionsopen in new window を利用します。

@Composable
fun LiveScreen() {
  val state = rememberMultiplePermissionsState(listOf(
    android.Manifest.permission.CAMERA,
    android.Manifest.permission.RECORD_AUDIO
  ))
  if (state.allPermissionsGranted) {
    Live()
  }
  else {
    Button(onClick = state::launchMultiplePermissionRequest) { 
      Text("Request permissions") 
    }
  }
}

@Composable
fun Live() { /* TODO */ }

2. CastifyApp, Broadcaster オブジェクトを作成する

/* in Live() */
val context = LocalContext.current
val app = remember { 
  CastifyApp(API_TOKEN, context = context) 
}
val broadcaster = remember { app.newBroadcaster() }

// クリーンアップ
DisposableEffect(Unit) {
  onDispose { broadcaster.close() }
}

CastifyApp は SDK の各機能にアクセスするための入り口となるオブジェクトです。これを作成するには Castify プラットフォームのプロジェクトに対応した API トークン (上記の API_TOKEN)を引数とし、そのコンストラクターを呼び出します。

Broadcaster はライブ配信を行う機能を持ったオブジェクトです。

3. プレビュー画面を準備する

配信映像を UI 上にプレビュー表示するためのビューを用意します。ビューには android.view.SurfaceView を利用します。

/* in Live() */
AndroidView(
  modifier = Modifier.clipToBounds(),
  factory = { SurfaceView(it).apply { broadcaster.previews.add(holder) } }
)

android.view.SurfaceViewComposable ではないので AndroidView を利用して Composable に変換します。また、そのインスタンスを broadcaster.previews に追加してプレビューの投影先として登録します。

4. ライブ配信する音声/映像の入力元を設定する

Broadcaster が配信するメディアの入力元には様々な種類があり、開発者はそのうちどれかを選択します。

このチュートリアルでは、音声はマイク、映像はカメラから入力したメディアを使うとします。

broadcaster.audio = AudioSource.Microphone()
broadcaster.video = VideoSource.Camera.findByFacing(VideoSource.Camera.Facing.FRONT)

また、以下のように各メディアに対応したエンコード設定を行う必要があります。

broadcaster.audioEncoderSetting = AudioEncoderSetting.AAC()
broadcaster.videoEncoderSetting = VideoEncoderSetting.H264(bps = 1_500_000)

TIP

audio, video プロパティの内容はどちらも任意のタイミングで変更することができます。

5. ライブ配信を開始するボタンを作成する

ライブ配信の開始または停止を行うボタンを配置します。

/* in Live() */
val started by remember { mutableStateOf(false) }
Button(onClick = { started = !started }) {
  Text(if (started) "Pause" else "Start")
}
LaunchedEffect(started) {
  if (started) {
    broadcaster.start()
  }
  else {
    broadcaster.pause()
  }
}

6. ライブ配信の状態を監視する

Broadcaster#start() の実行により Castify プラットフォームの配信サーバーとの接続が行われ、ライブ配信が開始します。

ライブ配信はネットワークを介した非同期的な処理なため、関数を呼び出してから実際に開始されるまでには時間的なラグがあります。また、ネットワークの問題により配信が途中で切れてしまうこともあります。こうした非同期的なイベントは全て、以下のように Broadcaster に登録したコールバックを通してアプリ側で通知を受けることができます。

val observer = remember { MyBroadcasterObserver() }
val broadcaster = remember { 
  app.newBroadcaster().apply { callback = observer }
}
LaunchedEffect(observer.state) {
  Toast.makeText(context, "State: ${observer.state.name}", Toast.LENGTH_LONG).show()
}

以下はコールバックの実態である MyBroadcasterObserver の実装例です。

class MyBroadcasterObserver : Broadcaster.Callback {

  var state by mutableStateOf(Broadcaster.State.Closed)

  override fun onStateChange(host: Broadcaster, state: Broadcaster.State, cause: Throwable?) {
    this.state = state
    if (cause != null) {
      Log.e("demo", "Broadcaster has stopped due to an error!", cause)
    }
  }

  var broadcast by mutableStateOf(null as Broadcast?)

  override fun onBroadcast(host: Broadcaster, broadcast: Broadcast) {
    this.broadcast = broadcast
  }
}

onStateChange で受け取っている state は配信の状態を表しています。

  • Broadcaster.State.Closed
    • 配信サーバーと未接続
  • Broadcaster.State.WIP
    • 配信サーバーに接続を行っている状態
  • Broadcaster.State.Opened
    • 配信サーバーと接続され配信中

Broadcaster.State.Closed が発火するタイミングで cause に例外オブジェクトが渡ってくる場合があります。これは何らかの問題により配信が中断されてしまったことを示します。その例外オブジェクトの内容を確認することで原因を突き止めることが可能です。

また onBroadcast は Castify プラットフォーム上に実際の配信が作成されたときに呼び出され broadcast には外部から配信にアクセスするための情報が含まれています。詳細は Broadcast を参照ください。

ここまでで、ライブ配信を行うための最小限の実装は完了しました。

配信テストを行う

アプリをビルドして Android 端末にデプロイしライブ配信を開始するボタンを作成するで配置した配信ボタンを押して配信を開始してみてください。

成功するとコンソールopen in new windowから配信された映像を閲覧することができます。

最終更新日: