映像の送信と受信

Sora Unity SDK で映像を送信・受信する際に必要となる設定や API について説明します。

デバイスカメラ・Unity カメラの切り替え、ミュート操作、受信映像の描画までを確認できるようになっています。

映像送信と受信の提供機能について

Sora Unity SDK では以下を提供しています。

  • デバイスカメラを映像送信に利用する

  • Unity カメラを映像送信に利用する

  • 任意の Texture を映像送信に利用する

  • 映像送信を一時停止する (ソフトミュート)

  • カメラデバイスを利用しない (ハードミュート)

  • 利用可能なカメラデバイス一覧を取得する

  • 受信した映像を任意のテクスチャに描画する

  • 映像コーデックの指定をする

  • ハードウェアアクセラレーションの利用を指定する

プラットフォームと制限

  • Sora.OnCapturerFrame は Android では利用できません

  • iOS や macOS ではカメラアクセス権限が無い場合、デバイス列挙や接続処理が失敗するため権限を付与したうえで利用してください

  • Unity カメラ送信や任意のテクスチャを送信する場合は RenderTextureFormat.BGRA32 が必要となるため、プロジェクト設定で BGRA32 を有効にしてください

映像関連の API リファレンス

Sora.Config

Sora.Config クラスは Sora インスタンスの接続設定を定義します。映像送信に関する主なプロパティは以下の通りです。

  • Video: 映像トラックの送信を有効にするかどうか。false にすると映像送信を停止します

  • NoVideoDevice: 物理カメラデバイスを利用しない場合に true を設定します

  • VideoBitRate: 映像送信のビットレート (kbps)。省略時は接続する Sora のデフォルト値になります

  • CameraConfig: 使用するカメラソースをまとめた Sora.CameraConfig のインスタンス

  • VideoCodecType: 利用する映像コーデックを指定します。省略時は Sora のデフォルト値になります

  • VideoCodecImplementation: 利用する映像エンコーダー / デコーダーを指定します。省略時は Internal (libwebrtc 標準) になります

  • VideoVp9Params: VP9 コーデックのパラメーターを JSON 文字列で指定します。空文字や不正な値の場合は INVALID-MESSAGE エラーになります。

  • VideoAv1Params: AV1 コーデックのパラメーターを JSON 文字列で指定します。空文字や不正な値の場合は INVALID-MESSAGE エラーになります。

  • VideoH264Params: H.264 コーデックのパラメーターを JSON 文字列で指定します。空文字や不正な値の場合は INVALID-MESSAGE エラーになります。

Sora.CameraConfig

Sora.CameraConfig クラスはカメラソースを定義します。プロパティは以下の通りです。

  • CapturerType: カメラの種類を指定します (DeviceCamera / UnityCamera / Texture のいずれかでデフォルトは DeviceCamera)

  • UnityCamera: Unity カメラを送信する場合に指定します

  • UnityCameraRenderTargetDepthBuffer: Unity カメラのレンダーターゲットに確保する深度バッファビット数を指定します (デフォルトは 16)

  • VideoCapturerDevice: 利用する物理カメラの DeviceName もしくは UniqueName を指定します(空文字の場合はデフォルトデバイスを利用します)

  • VideoWidth: 映像の幅を指定します(デフォルトは 640)

  • VideoHeight: 映像の高さを指定します(デフォルトは 480)

  • VideoFps: 映像のフレームレートを指定します(デフォルトは 30)

  • Texture: RenderTexture を送信する場合に指定します

Sora.CameraConfig のヘルパーメソッド

Sora.CameraConfig には設定生成を簡単にするヘルパーメソッドが用意されています。

各プロパティを個別に設定する代わりに、これらのメソッドを使うことで適切な設定のインスタンスを簡単に作成できます。

物理カメラを使用する場合:

Sora.CameraConfig.FromDeviceCamera(string videoCapturerDevice, int videoWidth, int videoHeight, int videoFps)

端末の内蔵カメラ、外付け Web カメラなどの物理カメラを送信ソースとして設定します。

  • videoCapturerDevice: Sora.GetVideoCapturerDevices() で取得したデバイスの DeviceName または UniqueName を指定

  • videoWidth, videoHeight: 送信する映像の解像度(例:1280 x 720)

  • videoFps: 送信するフレームレート(例:30 fps)

Unity カメラを使用する場合:

Sora.CameraConfig.FromUnityCamera(UnityEngine.Camera unityCamera, int unityCameraRenderTargetDepthBuffer, int videoWidth, int videoHeight, int videoFps)

Unityシーン内のカメラが映している内容を送信ソースとして設定します。ゲーム画面やCG映像を配信したい場合に使用します。

  • unityCamera: 送信したい Unity の Camera コンポーネント(例: Camera.main

  • unityCameraRenderTargetDepthBuffer: RenderTexture 深度バッファビット数(通常は16または24)

  • videoWidth, videoHeight: 送信する映像の解像度

  • videoFps: 送信するフレームレート

Texture を使用する場合:

Sora.CameraConfig.FromTexture(UnityEngine.Texture texture, int videoFps)

Unity で生成・管理している任意の Texture を送信ソースとして指定します。

  • texture: 送信したい TextureRenderTexture 推奨)

  • videoFps: 送信するフレームレート

ヘルパーメソッドを利用すると CapturerType に応じた必要な設定を利用することができます。

Sora クラスの映像関連 API

  • Sora.Connect(Sora.Config config): 指定した設定で接続します

  • Sora.VideoEnabled: ソフトミュートのためのトグルプロパティ

  • Sora.SwitchCamera(Sora.CameraConfig cameraConfig): 接続中に送信カメラを切り替えます。NoVideoDevice の設定に依存せず呼び出します

  • Sora.OnRender(): Unity カメラを利用する場合に WaitForEndOfFrame 後に呼び出してフレームを送信します

  • Sora.OnCapturerFrame: キャプチャ直後のフレームを受け取るコールバック。別スレッドで実行されます

  • Sora.GetVideoCapturerDevices(): 利用可能なカメラデバイス一覧を Sora.DeviceInfo[] で取得します (列挙に失敗した場合は null が返ります)

  • Sora.OnAddTrack / Sora.OnRemoveTrack: 受信映像トラックの追加・削除時に通知されます

  • Sora.GetVideoTrackFromVideoSinkId(uint videoSinkId): videoSinkId から対応する VideoTrack を取得します。指定した videoSinkId に対応する VideoTrack が見つからない場合は例外をスローします

  • Sora.VideoTrack: 映像トラックを表します。 Sora.GetVideoTrackFromVideoSinkId()Sora.OnMediaStreamTrack で取得できます

    • VideoTrack.GetVideoSinkId(Sora sora): この VideoTrack に対応する videoSinkId を取得します

  • Sora.RenderTrackToTexture(uint videoSinkId, Texture texture): videoSinkId で受信した映像を texture にレンダリングします。送信中の場合、自身の videoSinkId を指定すれば送信映像もレンダリングできます

  • Sora.DispatchEvents(): メインスレッドでイベントを処理するために Update() などから呼び出します

利用方法

接続と送信設定

以下は基本的な送信設定の例です。例では既に初期化済みの Sora インスタンス (sora) を利用しています。

// Inspector などで取得した設定値をもとに送信設定を組み立てる例
Sora sora; // 初期化済みの Sora インスタンス
var signalingUrl = "wss://example.com/signaling"; // シグナリング URL
var channelId = "sora"; // チャンネル ID
var selectedDevice = "camera_unique_name"; // 利用するデバイスカメラ名
Camera capturedCamera = Camera.main; // Unity カメラ送信時に利用するカメラ
var renderTargetDepth = 16; // RenderTexture の深度バッファビット数
int videoWidth = 1280; // 映像の幅
int videoHeight = 720; // 映像の高さ
int videoFps = 30; // 映像のフレームレート
int videoBitRate = 1500; // 映像のビットレート (kbps)
var useUnityCamera = false; // true なら Unity カメラを送信
var disableDevice = false; // true なら NoVideoDevice を有効化

var config = new Sora.Config
{
    SignalingUrl = signalingUrl,
    ChannelId = channelId,
    Role = Sora.Role.Sendrecv,
    Video = true, // 映像送信を有効化
    NoVideoDevice = disableDevice, // 物理カメラデバイスを利用しない場合は true
    VideoBitRate = videoBitRate, // 送信ビットレート (kbps)
};

// 物理カメラか Unity カメラかを判定してカメラソースを設定
// 使用するカメラに応じて、ヘルパーメソッドで CameraConfig を生成します
// useUnityCamera が true かつ capturedCamera が null でない場合は Unity カメラを使用します
// それ以外の場合、物理カメラデバイスを使用 (Web カメラなどの実カメラを送信)します
config.CameraConfig = useUnityCamera && capturedCamera != null
    ? Sora.CameraConfig.FromUnityCamera(capturedCamera, renderTargetDepth, videoWidth, videoHeight, videoFps)
    : Sora.CameraConfig.FromDeviceCamera(selectedDevice, videoWidth, videoHeight, videoFps);

sora.Connect(config);

利用可能なカメラデバイスの列挙

Sora.GetVideoCapturerDevices() を使うと利用可能なカメラデバイスを取得できます。

戻り値は Sora.DeviceInfo[] で、列挙が失敗した場合は null が返ります。

取得したデバイス名、UniqueName を使って Sora.CameraConfig.VideoCapturerDevice に設定できます。

ここでは取得結果をログに出力する例を示します。

void DumpDeviceInfo(string label, Sora.DeviceInfo[] devices)
{
    if (devices == null || devices.Length == 0)
    {
        // デバイスが見つからない場合の処理
        Debug.LogWarning($"{label}: device not found");
        return;
    }

    foreach (var device in devices)
    {
        // DeviceName と UniqueName をログに出力
        Debug.Log($"{label}: DeviceName={device.DeviceName}, UniqueName={device.UniqueName}");
    }
}

void Start()
{
    // 例として起動時にデバイス一覧を出力
    DumpDeviceInfo("video capturer devices", Sora.GetVideoCapturerDevices());
}

映像コーデックとビットレートの設定

映像品質に影響する主要な設定について説明します。

コーデックの指定

Sora.Config.VideoCodecType を使用して利用する映像コーデックを指定できます。省略した場合は Sora サーバーのデフォルト値が使用されます。

利用可能なコーデック:

  • Sora.VideoCodecType.VP9

  • Sora.VideoCodecType.VP8

  • Sora.VideoCodecType.H264

  • Sora.VideoCodecType.AV1

  • Sora.VideoCodecType.H265

コーデックを利用する前に、クライアントがそのエンコーダ/デコーダを利用可能か Sora.GetVideoCodecCapability() で確認してください。

Encoder / Decodertrue でないコーデックを指定すると、接続処理や切断処理でクラッシュする恐れがあります。

コーデックパラメーターの指定

各コーデックには詳細なパラメーターを設定できます。

VP9 のパラメーター

Sora.Config.VideoVp9Params で VP9 コーデックのプロファイル ID を JSON 文字列として指定できます。

// SoraSample.cs を参考にした VP9 のパラメーターを設定するサンプルコード
// enableVideoVp9Params が true の場合に VP9 パラメーターを設定します

// VP9 パラメーターを設定する場合有効にする
public bool enableVideoVp9Params = false;
// VP9 パラメーターを設定 0, 1, 2, 3 が利用可能
public int videoVp9ParamsProfileId;

// VP9 パラメーターを設定する場合は JSON 文字列を設定する
string videoVp9ParamsJson = "";
if (enableVideoVp9Params)
{
    var vp9Params = new VideoVp9Params()
    {
        profile_id = videoVp9ParamsProfileId
    };
    videoVp9ParamsJson = JsonUtility.ToJson(vp9Params);
}

var config = new Sora.Config
{
    SignalingUrl = signalingUrl,
    ChannelId = channelId,
    VideoCodecType = Sora.VideoCodecType.VP9,
    VideoVp9Params = videoVp9ParamsJson,
};
AV1 のパラメーター

Sora.Config.VideoAv1Params で AV1 コーデックのプロファイルを JSON 文字列として指定できます。

// SoraSample.cs を参考にした AV1 のパラメーターを設定するサンプルコード
// enableVideoAv1Params が true の場合に AV1 パラメーターを設定します

// AV1 パラメーターを設定する場合有効にする
public bool enableVideoAv1Params = false;
// AV1 パラメーターを設定 0, 1, 2 が利用可能
public int videoAv1ParamsProfile;

// AV1 パラメーターを設定する場合は JSON 文字列を設定する
string videoAv1ParamsJson = "";
if (enableVideoAv1Params)
{
    var av1Params = new VideoAv1Params()
    {
        profile = videoAv1ParamsProfile
    };
    videoAv1ParamsJson = JsonUtility.ToJson(av1Params);
}

var config = new Sora.Config
{
    SignalingUrl = signalingUrl,
    ChannelId = channelId,
    VideoCodecType = Sora.VideoCodecType.AV1,
    VideoAv1Params = videoAv1ParamsJson,
};
H.264 のパラメーター

Sora.Config.VideoH264Params で H.264 コーデックのプロファイルレベル ID を JSON 文字列として指定できます。

// SoraSample.cs を参考にした H.264 のパラメーターを設定するサンプルコード
// enableVideoH264Params が true の場合に H.264 パラメーターを設定します

// H.264 パラメーターを設定する場合有効にする
public bool enableVideoH264Params = false;
// H.264 パラメーターを設定
// 例: "42e01f" はレベル 3.1
public string videoH264ParamsProfileLevelId = "";

// H.264 パラメーターを設定する場合は JSON 文字列を設定する
string videoH264ParamsJson = "";
if (enableVideoH264Params)
{
    var h264Params = new VideoH264Params()
    {
        profile_level_id = videoH264ParamsProfileLevelId
    };
    videoH264ParamsJson = JsonUtility.ToJson(h264Params);
}

var config = new Sora.Config
{
    SignalingUrl = signalingUrl,
    ChannelId = channelId,
    VideoCodecType = Sora.VideoCodecType.H264,
    VideoH264Params = videoH264ParamsJson,
};

ビットレートの設定

Sora.Config.VideoBitRate で送信ビットレート(kbps)を指定できます。省略した場合は Sora サーバーのデフォルト値が使用されます。

var config = new Sora.Config
{
    SignalingUrl = signalingUrl,
    ChannelId = channelId,
    VideoCodecType = Sora.VideoCodecType.H264, // コーデックを指定
    VideoBitRate = 1500, // ビットレート(kbps)を指定
};

sora.Connect(config);

VideoCodecImplementation を使用してエンコーダー/デコーダーの実装を指定することも可能です。

詳細は エンコーダー / デコーダーの指定 を参照してください。

Unity カメラを送信する場合

Unity カメラを映像送信に利用する場合の設定と実装方法を説明します。

設定方法

Unity カメラを使用する場合は CameraConfigCapturerType.UnityCamera を指定し、送信したいカメラを設定します。

var config = new Sora.Config
{
    SignalingUrl = signalingUrl,
    ChannelId = channelId,
    CameraConfig = new Sora.CameraConfig()
    {
        CapturerType = Sora.CapturerType.UnityCamera,
        UnityCamera = capturedCamera, // 送信したいCamera
        VideoFps = 30,
        VideoWidth = 1280,
        VideoHeight = 720,
    },
};

または、ヘルパーメソッドを使用することもできます:

config.CameraConfig = Sora.CameraConfig.FromUnityCamera(capturedCamera, 16, 1280, 720, 30);

フレーム送信の実装

Unity カメラを送信する際は WaitForEndOfFrame の最後で Sora.OnRender() を呼び出します。

この処理はコルーチンとして実装し、Start() などで開始してください。

void Start()
{
    StartCoroutine(Render());
}

IEnumerator Render()
{
    while (true)
    {
        yield return new WaitForEndOfFrame(); // Unity の描画完了を待機
        if (sora != null)
        {
            // OnRender() は必ず WaitForEndOfFrame の後に呼び出すこと
            sora.OnRender(); // Unity カメラの映像フレームを Sora に転送
        }
    }
}

Texture を送信する場合

CapturerType.Texture を使うと、任意の Texture を映像トラックとして送信できます。ポストプロセス済みのゲーム画面やコンピュートシェーダーの結果などを配信したい場合に有効です。

注釈

Texture2D なども指定できますが、ピクセルを書き換えるたびに Unity が内部テクスチャを作り直し、以前に取得した GetNativeTexturePtr() が無効になります。 Sora に渡したポインタが切れてしまうため、動的に更新する場合は RenderTexture を Sora に渡し、Graphics.Blit(Texture2D, RenderTexture) などで内容を反映させる運用を推奨します。

設定例

// 生成した RenderTexture を Sora に送信し、かつ画面に表示するサンプル
// 事前に RenderTexture を用意していない場合でも自動で生成します

Sora sora;
// RenderTexture を送る(Inspector で割り当て可。未設定なら生成)
public RenderTexture captureTargetTexture;
// captureTargetTexture の内容を画面表示する Texture
public Texture2D stagingTexture;

InitSora();

GetVideoSize(videoSize, out var videoWidth, out var videoHeight);

// RenderTexture 未割り当てなら生成
if (captureTargetTexture == null)
{
    captureTargetTexture = new RenderTexture(videoWidth, videoHeight, 16, RenderTextureFormat.BGRA32);
    captureTargetTexture.Create();
}
else if (!captureTargetTexture.IsCreated())
{
    captureTargetTexture.Create();
}

// stagingTexture もサイズを揃える
if (stagingTexture == null ||
    stagingTexture.width != captureTargetTexture.width ||
    stagingTexture.height != captureTargetTexture.height)
{
    stagingTexture = new Texture2D(captureTargetTexture.width, captureTargetTexture.height, TextureFormat.BGRA32, false);
}

// stagingTexture を更新して RenderTexture に反映(内容を書き換えた直後に呼ぶ)
stagingTexture.Apply(false, false);
Graphics.Blit(stagingTexture, captureTargetTexture);

// RenderTexture を送る設定
var cameraConfig = Sora.CameraConfig.FromTexture(captureTargetTexture, videoFps);

var config = new Sora.Config()
{
    SignalingUrl = signalingUrl,
    ChannelId = channelId,
    CameraConfig = cameraConfig,
};
sora.Connect(config);

// 毎フレーム更新したい場合は Update() で
void Update()
{
    if (stagingTexture != null && captureTargetTexture != null)
    {
        stagingTexture.Apply(false, false);
        Graphics.Blit(stagingTexture, captureTargetTexture);
    }
}

カメラの切り替え

接続中にデバイスカメラとUnityカメラを切り替える場合は SwitchCamera() を使用します。

// クラスのフィールドとして定義
Sora sora;
public Camera capturedCamera;
public string videoCapturerDevice = "";

public void SwitchToUnityCamera()
{
    if (sora == null) return;
    sora.SwitchCamera(Sora.CameraConfig.FromUnityCamera(capturedCamera, 16, 1280, 720, 30));
}

public void SwitchToDeviceCamera()
{
    if (sora == null) return;
    sora.SwitchCamera(Sora.CameraConfig.FromDeviceCamera(videoCapturerDevice, 1280, 720, 30));
}

ソフトミュートとハードミュート

ミュートにはデバイスを維持するソフトミュートと、デバイス自体を利用しないハードミュートの 2 種類があります。

ソフトミュートは Sora.VideoEnabled を切り替えて行います。この場合カメラデバイスは解放されません。

public void ToggleVideo()
{
    if (sora == null) return;
    sora.VideoEnabled = !sora.VideoEnabled;
}

カメラデバイスを利用しないハードミュートは接続時に Sora.Config.NoVideoDevicetrue に設定することで行います。

警告

送信中ハードウェアミュートに切り替えることはできず、再接続をする必要があります。

Sora.SwitchCamera() を呼び出すとカメラを取得しに行くため、ハードミュート中は呼び出さないでください。

意図せずカメラを取得してミュートが解除されることがあります。

var config = new Sora.Config
{
    SignalingUrl = signalingUrl,
    ChannelId = channelId,
    NoVideoDevice = true, // 物理カメラデバイスを利用しない
};

sora.Connect(config);

映像受信の処理

受信した映像トラックは OnAddTrack / OnRemoveTrack で管理し、RenderTrackToTexture で描画します。

connectionId が空文字の場合は自身の映像です。

この例は SoraUnitySdkExamples の SoraSample.cs にもありますのでそちらも参考にしてください。

// 受信映像トラックを管理するためのDictionary
Dictionary<string, GameObject> tracks = new Dictionary<string, GameObject>();

// 映像トラックが追加された時のコールバック
sora.OnAddTrack = (videoSinkId, connectionId) =>
{
    // UI要素を生成(baseContentはプリファブとして事前に用意)
    // Hierarchy に Canvas を作成して RawImage を対象にすると簡単です
    var obj = Instantiate(baseContent);
    obj.name = $"track {videoSinkId}";
    obj.transform.SetParent(scrollViewContent.transform);
    obj.SetActive(true);

    // 映像を描画するためのテクスチャを生成
    var image = obj.GetComponent<UnityEngine.UI.RawImage>();
    image.texture = new Texture2D(320, 240, TextureFormat.RGBA32, false);

    // トラック管理用のDictionaryに追加
    tracks[videoSinkId] = obj;
};

// 映像トラックが削除された時のコールバック
sora.OnRemoveTrack = (videoSinkId, connectionId) =>
{
    if (!tracks.TryGetValue(videoSinkId, out var obj)) return;

    // テクスチャとGameObjectを適切に破棄
    var image = obj.GetComponent<UnityEngine.UI.RawImage>();
    Destroy(image.texture);
    Destroy(obj);
    tracks.Remove(videoSinkId);
};

void Update()
{
    if (sora == null) return;

    // 受信イベントをメインスレッドで処理(必須)
    sora.DispatchEvents();

    // 各トラックの映像をテクスチャに描画
    foreach (var pair in tracks)
    {
        var image = pair.Value.GetComponent<UnityEngine.UI.RawImage>();
        // 受信映像をテクスチャに描画(毎フレーム呼び出す)
        sora.RenderTrackToTexture(pair.Key, image.texture);
    }
}
© Copyright 2024, Shiguredo Inc. Created using Sphinx 9.0.4