音声データ取得機能

Sora Unity SDK では音声トラックから非圧縮の音声データを取得する仕組みを提供しています。

音声トラックを表す AudioTrackIAudioTrackSink を関連付けると、音声データを音声トラックごとにコールバックで受け取ることができます。

ここでは、音声データ取得機能と利用方法について説明します。基本的な音声送受信については 音声の送信と受信 を参照してください。

音声データの形式

コールバックで取得できる音声データは非圧縮の 16 bit PCM (Pulse Code Modulation) 形式です。

IAudioTrackSink インターフェースは以下のように定義されています。

public interface IAudioTrackSink
{
    void OnData(short[] data, int bitsPerSample, int sampleRate,
                int numberOfChannels, int numberOfFrames,
                long? absoluteCaptureTimestampMs);
}

OnData() コールバックのパラメータ:

  • data: PCM 形式の音声データ (short 配列)

  • bitsPerSample: 1 サンプル当たりのビット数 (libwebrtc では常に 16)

  • sampleRate: サンプルレート (単位: Hz)

  • numberOfChannels: 音声データのチャンネル数 (モノラルなら 1、ステレオなら 2)

  • numberOfFrames: data に含まれるフレーム数

  • absoluteCaptureTimestampMs: キャプチャ時刻 (ミリ秒)

送信音声データを取得する

Sora.SenderAudioTrackSinkIAudioTrackSink を実装したクラスを設定すると、送信中の音声データを取得できます。

class AudioDataSink : Sora.IAudioTrackSink
{
    public void OnData(short[] data, int bitsPerSample, int sampleRate,
                       int numberOfChannels, int numberOfFrames,
                       long? absoluteCaptureTimestampMs)
    {
        // PCM 音声データを処理
        Debug.Log($"送信音声データ: sampleRate={sampleRate}, channels={numberOfChannels}, frames={numberOfFrames}");
    }
}

void Start()
{
    sora.SenderAudioTrackSink = new AudioDataSink();
}

受信音声データを取得する

Sora.OnMediaStreamTrack コールバックで受信した AudioTrackIAudioTrackSink を追加すると、各トラックの音声データを個別に取得できます。

AudioTrack の識別方法

IAudioTrackSink.OnData() で受け取る音声データがどのトラックのものか判断するには、OnMediaStreamTrack コールバックで渡される AudioTrackconnectionId を使用してください。

connectionId は Sora サーバーが払い出すコネクション ID です。

AudioTrackSink と AudioTrack の関連付けについて

  • IAudioTrackSinkAudioTrack の関連付けは 1:1 を前提に設計されています

  • 1 つの IAudioTrackSink を複数の AudioTrack に同時に関連付ける 1:N の利用は想定していません

  • 一方で、1 本の AudioTrack に対して複数の IAudioTrackSink を関連付けることは可能で、各シンクは独立して同一のトラックから音声データを受け取ります

実装例

以下は受信音声トラックごとに音声データを取得する例です。

class AudioTrackSink : Sora.IAudioTrackSink
{
    public void OnData(short[] data, int bitsPerSample, int sampleRate,
                       int numberOfChannels, int numberOfFrames,
                       long? absoluteCaptureTimestampMs)
    {
        // PCM 音声データを処理
        Debug.Log($"受信音声データ: sampleRate={sampleRate}, channels={numberOfChannels}, frames={numberOfFrames}");
    }
}

Dictionary<string, AudioTrackSink> audioSinks = new Dictionary<string, AudioTrackSink>();

void Start()
{
    sora.OnMediaStreamTrack = (transceiver, track, connectionId) =>
    {
        if (track.Kind == Sora.MediaStreamTrack.AudioKind)
        {
            var audioTrack = track as Sora.AudioTrack;
            var sink = new AudioTrackSink();
            audioTrack.AddSink(sora, sink);
            audioSinks[connectionId] = sink;
        }
    };

    sora.OnRemoveMediaStreamTrack = (receiver, track, connectionId) =>
    {
        if (track.Kind == Sora.MediaStreamTrack.AudioKind && audioSinks.ContainsKey(connectionId))
        {
            var audioTrack = track as Sora.AudioTrack;
            audioTrack.RemoveSink(sora, audioSinks[connectionId]);
            audioSinks.Remove(connectionId);
        }
    };
}
© Copyright 2024, Shiguredo Inc. Created using Sphinx 9.0.4