見出し画像

ミュート・寝落ち配信フィルターが欲しい! - REALITY Advent Calendar #10

こんにちは、REALITY Advent Calendar 2022の10日目担当、iOSチームのますずみです。今回は開発合宿で作ったミュート・寝落ち配信フィルターについての話です。

背景

REALITYの配信一覧を見ていると、たまに「ミュート放置」とタイトルに入った配信枠を見かけることがあります。こういった枠でよくあるパターンとしては、「視聴者が心置きなく視聴ボーナスポイントを回収できるようにしよう」といった配信者の思いやりによるものではあるのですが、時と場合によっては(その日のポイント回収が終わった後とか…)ユーザの利便性向上のために配信一覧における表示を調整したいなーと思うことがあります。

また、配信しているうちに寝落ちしてしまい、枠に入ってきてくれた視聴者に反応できない形になってしまうことがあります。

この辺りの困り感をなんとかすべく、開発合宿でミュート・寝落ち配信をフィルタリングするための仕組みの実装に取り組みました。

作ったもの

というわけで、今回の合宿では「ミュート・寝落ち配信をリアルタイムに検出する」機能の実装を行いました。

「ミュート・寝落ち検出くん」というあんまりなネーミング


ミュート・寝落ち検出機能はCloud Dataflowのストリーミングパイプラインとして実装されています。Cloud Pub/Sub Topicにリアルタイムで流れる配信データを受け取り、ミュート・寝落ち判定処理を行う形式です。

どうやってミュートを検出するか

配信中にミュートにすると、内部的には「ClientがServerに音声データを送らなくなる」ことになります。音量ゼロのデータを送るわけではなく、「何も送らない」です。
つまり、ミュートの検出のためには「音声データがServerに送られてきているかどうか」を見ればいいわけですね。

どうやって寝落ちを検出するか

一方で、寝落ちの検出はやや複雑です。
まず「配信中に寝落ちするとどうなるか」を考えてみましょう。

寝違えそう

寝落ちのスタイルは人それぞれだと思いますが、一旦↑のイラストのようなケースを考えてみましょう。
この場合内部的にどうなるかというと、デバイスのカメラからフレームアウトしているわけで、フェイストラッキングが正常に動作せず、「アバターが寝違えそうな姿勢で固まる」ことになると考えられます。つまり、「フェイストラッキングがソースのアバターモーションを確認する」ことで寝落ちしているかどうかを判定できそうです。

実装

具体的な実装としては下記のようになります(※一部note掲載用に改変しています)
Cloud Dataflow上で動かすためのApache Beam PipelineをKotlinで実装する形です。

    @JvmStatic
    fun main(args: Array<String>) {
        val options = PipelineOptionsFactory
            .fromArgs(*args)
            .withValidation()
            .`as`(Options::class.java)
        val subscription = options.subscription
        val pipeline = Pipeline.create(options)

        pipeline

            // 1: Cloud Pub/Sub Topicからデータを読み出す
            .apply(PubsubIO.readMessagesWithAttributesAndMessageId().fromSubscription(subscription))

            // 2: 集計のためにデータのフォーマットを整える
            .apply(MapElements
                .into(TypeDescriptor.of(RawDataMessage::class.java))
                .via(ProcessFunction { RawDataMessage(it.messageId, System.currentTimeMillis(), it.payload) }))
            .setCoder(AvroCoder.of(RawDataMessage::class.java))
            .apply(MapElements
                .into(TypeDescriptor.of(HabaneroMessage::class.java))
                .via(ProcessFunction { it.toHabaneroMessage() }))
                .setCoder(AvroCoder.of(HabaneroMessage::class.java))
            .setCoder(AvroCoder.of(HabaneroMessage::class.java))
            .apply(WithKeys.of { it.liveId })
            .setCoder(KvCoder.of(VarLongCoder.of(),AvroCoder.of(HabaneroMessage::class.java) ))

            // 3: 10秒間隔・60秒のホッピングウィンドウで処理する
            .apply(Window.into(
                SlidingWindows.of(Duration.standardSeconds(60))
                    .every(Duration.standardSeconds(10))))
            .apply(GroupByKey.create())

            // 4: 配信ごとにグルーピングして ミュート・寝落ちを判定する
            .apply(ParDo.of(Aggregate()))

        pipeline.run()
    }

今後の展望

今回の開発合宿では、ミュート・寝落ち配信のフィルタリングを行うための下地を作ることができました。今後何らかの形でユーザーの皆様にサービスの機能として提供することも検討しています。

最後に

明日のアドベントカレンダーは、サーバーエンジニアの@sola-msrさんによる「REALITYのコマンドいろいろ実装してみた!」です。お楽しみに!