UnityでRTSPビデオストリーミング再生

# はじめに
RTSP配信された映像をUnityでストリーミングしたい場合があります。
しかし、Unityには標準でRTSP再生をサポートしているコンポーネントはありません。
今回は[gujadot](https://github.com/gujadot)氏の[RTSP_Unity_Plugin](https://github.com/gujadot/RTSP_Unity_Plugin)を用いてUnityにおいてRTSPストリーミングを導入する方法を解説します。

# 用いるソフトウェア
* Unity 2019.2.8f1
* Visual Studio 2017
* [RTSP_Unity_Plugin](https://github.com/gujadot/RTSP_Unity_Plugin)
* [FFmpeg](http://ffmpeg.zeranoe.com/builds/)

# 実装手順
## DLL生成編
1. Visual StudioにVisual C++をインストールしておく。
2. [FFmpegのダウンロードページ](http://ffmpeg.zeranoe.com/builds/)にアクセスする。dev/shareの両方をダウンロード。
3. RTSP_Unity_Pluginをダウンロード。RTSPUnityPlugin.vcxproj(VC++ Project)をVisual Studioで開く。
4. プロジェクトフォルダ内に`ffmpeg`フォルダを作成する。以下のような構成になる。![](https://i.imgur.com/pn8792M.png)
5. 作成したffmpeg以下にffmpeg/include、ffmpeg/libの2フォルダを作成する。<br>各フォルダに次の通り、ダウンロードしたFFmpegの内容をコピーする。<br>`ffmpeg/include <- copy <- FFmpeg/win64-dev/include`<br>`ffmpeg/lib <- copy <- FFmpeg/win64-dev/lib と FFmpeg/win64-shared/bin`
6. Visual StudioのエクスプローラからRTSPUnityPluginのプロパティを開く。
7. [構成プロパティ]-[C/C++]-[全般] を開く。
8. [追加のインクルードディレクトリ]に以下を入力。

    ```
$(SolutionDir)\ffmpeg\include;%(AdditionalIncludeDirectories)
```

9. [追加の#usingディレクトリ]に以下を入力。

    ```
$(SolutionDir)\ffmpeg\lib
```
9. [構成プロパティ]-[リンカー]-[全般] を開く。
10. [追加のライブラリディレクトリ]に以下を入力。

    ```
$(SolutionDir)\ffmpeg\lib;%(AdditionalLibraryDirectories)
```
9. [構成プロパティ]-[デバッグ] を開く。
9. [コマンド]に以下を入力。

    ```
$(TargetPath)
```
10. [構成プロパティ]-[リンカー]-[入力] を開く。
9. [追加の依存ファイル]に以下を入力。

    ```
avcodec.lib
avdevice.lib
avfilter.lib
avformat.lib
avutil.lib
postproc.lib
swresample.lib
swscale.lib
```
11. [構成プロパティ]-[ビルドイベント]-[リンク前のイベント] を開く。
* [コマンドライン]に以下を入力。

    ```
copy "$(SolutionDir)ffmpeg\lib\avcodec-57.dll" "$(TargetDir)"
copy "$(SolutionDir)ffmpeg\lib\avdevice-57.dll" "$(TargetDir)"
copy "$(SolutionDir)ffmpeg\lib\avfilter-6.dll" "$(TargetDir)"
copy "$(SolutionDir)ffmpeg\lib\avformat-57.dll" "$(TargetDir)"
copy "$(SolutionDir)ffmpeg\lib\avutil-55.dll" "$(TargetDir)"
copy "$(SolutionDir)ffmpeg\lib\postproc-54.dll" "$(TargetDir)"
copy "$(SolutionDir)ffmpeg\lib\swresample-2.dll" "$(TargetDir)"
copy "$(SolutionDir)ffmpeg\lib\swscale-4.dll" "$(TargetDir)"
```
12. ビルドする。

以上の手順を踏むと、`build\x64\Release\RTSPUnityPlugin.dll`が生成されます。

## Unity編
### DLLの配置
上記の手順で生成されたReleaseフォルダをAssets/Plugins下に配置する。
### オブジェクトへの適用
ストリームしたいマテリアルのオブジェクトに次のスクリプトをアタッチする。
(setRTSPTexture.cs)

```csharp:setRTSPTexture.cs
using UnityEngine;
using System;
using System.Collections;
using System.Runtime.InteropServices;


public class SetRTSPTexture : MonoBehaviour {

    [DllImport("RTSPUnityPlugin")]
    private static extern void SetTimeFromUnity(float t);
    [DllImport("RTSPUnityPlugin")]
    private static extern IntPtr GetRenderEventFunc();
    [DllImport("RTSPUnityPlugin")]
    private static extern void SetTextureAsRTSPSink(string rtsp_uri,System.IntPtr texture, int h, int w);


    public string g_rtspUri = "rtsp://localhost:8554/stream";

    IEnumerator Start()
    {
        CreateTextureAndPassToPlugin();
        yield return StartCoroutine("CallPluginAtEndOfFrames");
    }

    private void CreateTextureAndPassToPlugin()
    {
        // Create a texture
        Texture2D tex = new Texture2D(256, 256, TextureFormat.RGBA32, false);
        // Set point filtering just so we can see the pixels clearly
        tex.filterMode = FilterMode.Point;
        // Call Apply() so it's actually uploaded to the GPU
        tex.Apply();

        // Set texture onto our material
        GetComponent<Renderer>().material.mainTexture = tex;

        // Pass texture pointer to the plugin
        SetTextureAsRTSPSink(g_rtspUri, tex.GetNativeTexturePtr(), tex.width, tex.height);
    }

    private IEnumerator CallPluginAtEndOfFrames()
    {
        while (true)
        {
            // Wait until all frame rendering is done
            yield return new WaitForEndOfFrame();

            // Set time for the plugin
            SetTimeFromUnity(Time.timeSinceLevelLoad);

            // Issue a plugin event with arbitrary integer identifier.
            // The plugin can distinguish between different
            // things it needs to do based on this ID.
            // For our simple plugin, it does not matter which ID we pass here.
            GL.IssuePluginEvent(GetRenderEventFunc(), 1);
        }
    }
}

```
これでUnityを実行すると、RTSPを受信してオブジェクトのテクスチャで再生されます。
## 最後に
このプラグインを用いれば[THETA](https://pluginstore.theta360.com/plugins/com.sciencearts.rtspstreaming/)などのネットワークカメラから映像をライブ配信できるので、Unityでビデオチャットのような面白い機能が実現できそうです。素晴らしいプラグインを提供してくださった[gujadot](https://github.com/gujadot)氏に感謝！