Azure App Serviceでソラカメのストリーミング再生を行う

# 概要
当社でMicrosoft Azureを使ったSaaSを提供していて、
そこでソラカメを活用した見守りサービスの開発を行っています。
AWSを使ったソラカメのストリーミングの事例を参考に、
Azureを使ってストリーミング再生を行う例を書きたいと思います。


# 環境など
開発環境
・Visual Studio 2022

実行環境
・Azure App Service
・Logic App

使用するフレームワーク、ライブラリ等
・ASP .NET Core(.NET 7)
・dash.js（ストリーミング用）


# システム構成
検証用に構築したシステム構成はこんな感じ。

![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/8dc37851-0cab-0931-f6b3-d7ee402f9bf2.png)

# 手順
ソラカメを既に運用している前提です。

## SORACOM プラットフォーム側の準備
①SAM ユーザー用ロール作成
ソラコムのコンソールで、APIアクセス用のロールを作成しておきます。
ここでは、SoracamWatcherRoleという名前のロールを作成しました。
ロールに設定する権限は以下のような感じ。

```json
{
  "statements": [
    {
      "effect": "allow",
      "api": [
        "SoraCam:*Device*",
        "OAuth2:authorize",
        "Auth:*"
      ]
    }
  ]
}
```
 
②SAM ユーザー作成
APIアクセス用のSAMユーザーを作成し、先ほど作成したロールを割り当てます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/bbcc40bd-4ac5-8fa1-47d4-0d8044a8de31.png)


③ SAMユーザーの認証キー生成
SAMユーザーの認証設定で、認証キーを生成します。
生成時に表示される認証キーIDと認証キーシークレットをAPI呼び出し時に使用するので
忘れずコピーしておいてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/62a0aa74-b7ec-280c-ebeb-438c4591407c.png)

## Azure Logic Apps
ソラコムのAPIを呼び出すための処理を、ここではLogic Apps上で実装しました。
⇒今後使いまわしするかはさておき・・・。

Logic Appsは三つにわけて作成しています。
一旦ソラカメAPIのラッパーの位置づけで作成しています。

・soracam_auth　SAMユーザーのAPIキーとトークン発行用
※本文のauthKeyとauthKeyIdには、先に生成した認証キーIDと認証キーシークレットを入れて下さい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/459a8188-57dc-46ba-9701-960ae2e933c7.png)


・soracam_devices　ソラカメのデバイスリスト（カメラリスト）取得用
※上記のLogic Appsで取得したAPIキーとトークンを引数で受け取ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/494ff6fc-d948-ac8f-2e5f-dc061695fea6.png)


・soracam_streaming　対象ソラカメのストリーミングURL取得用
※上記のLogic Appsで取得したAPIキーとトークン、対象カメラのデバイスIDを引数で受け取ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/9facdb82-f8c4-7e8e-bfa6-2816ef2b630f.png)

## Azure App Service
App ServiceはASP .NET Coreを使用したWebアプリをホストさせます。

Webアプリは、ページにデバイス（＝カメラ）のリストを表示させ、
選択されたカメラのストリーミング用URLを取得を行って
動画の表示を行います。

といっても、動画の再生はライブラリのdash.jsにお任せで、
実質はLogic Appsを呼び出すのみとなります。
※さらに言うと、今回の実装のみで言えば、Static Web Appsで充分ですが・・・

①まず、Logic Appを呼び出すために、HttpClientサービスを追加

```program.cs

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();

// この行を追加
builder.Services.AddHttpClient();

var app = builder.Build();

//・・・

```

②とりあえずIndexページにストリーム再生する処理を実装（ControllerはHome）。
変数名もエラー処理も適当すぎるのでご注意ください（汗）。

```HomeController.cs
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        private readonly IHttpClientFactory _httpClientFactory;

        public HomeController(ILogger<HomeController> logger,
            IHttpClientFactory httpClientFactory)
        {
            _logger = logger;
            _httpClientFactory = httpClientFactory;
        }

        public async Task<IActionResult> Index()
        {
            var httpClient = _httpClientFactory.CreateClient();

            // 認証
            var request = new HttpRequestMessage(
                        HttpMethod.Get,
                        "Logic Appsに作ったsoracam_authのURL");

            var response = await httpClient.SendAsync(request);
            if (!response.IsSuccessStatusCode)
            {
                // エラー処理
            }

            // コンテンツデータからトークンなどを取得
            using var contentStream = await response.Content.ReadAsStreamAsync();
            var authData = await JsonSerializer.DeserializeAsync<AuthData>(contentStream);

            // デバイスリスト取得
            var authParam = new DeviceListParam()
            {
                apiKey = authData.apiKey,
                token = authData.token,
            };

            var authDataJson = new StringContent(
                JsonSerializer.Serialize(authParam),
                Encoding.UTF8,
                Application.Json);

            var request2 = new HttpRequestMessage(
                        HttpMethod.Post,
                        "Logic Appsに作ったsoracam_devicesのURL")
            {
                Content = authDataJson
            };
           
            var response2 = await httpClient.SendAsync(request2);
            if (!response2.IsSuccessStatusCode)
            {
                // エラー処理
            }

            // コンテンツデータからデバイスリストを取得
            using var contentStream2 = await response2.Content.ReadAsStreamAsync();
            var devicesData = await JsonSerializer.DeserializeAsync<IEnumerable<DevicesData>>(contentStream2);

            ViewBag.DeviceList = new SelectList(devicesData, "deviceId", "name");

            return View();
        }


        [HttpPost]
        public async Task<IActionResult> GetStreamUrl(string deviceId)
        {
            var httpClient = _httpClientFactory.CreateClient();

            // 認証
            var request = new HttpRequestMessage(
                        HttpMethod.Get,
                        "Logic Appsに作ったsoracam_authのURL");

            var response = await httpClient.SendAsync(request);
            if (!response.IsSuccessStatusCode)
            {
                // エラー処理
            }

            // コンテンツデータからトークンなどを取得
            using var contentStream = await response.Content.ReadAsStreamAsync();
            var authData = await JsonSerializer.DeserializeAsync<AuthData>(contentStream);

            if (authData == null)
            {
                // エラー処理
            }

            // ストリーミングURL取得
            var authParam = new StreamURLParam()
            {
                apiKey = authData.apiKey,
                token = authData.token,
                deviceId = deviceId,
            };

            var authDataJson = new StringContent(
                JsonSerializer.Serialize(authParam),
                Encoding.UTF8,
                Application.Json);

            var request2 = new HttpRequestMessage(
                        HttpMethod.Post,
                        "Logic Appsに作ったsoracam_streamingのURL")
            {
                Content = authDataJson
            };

            var response2 = await httpClient.SendAsync(request2);
            if (!response2.IsSuccessStatusCode)
            {
                // エラー処理
            }

            using var contentStream2 = await response2.Content.ReadAsStreamAsync();
            var streamURLData = await JsonSerializer.DeserializeAsync<StreamURLData>(contentStream2);

            return Ok(streamURLData.playList[0].url);
        }

    }
```


## 実行画面
当社運営のレンタルスペース「BABY ROOM」の防犯用カメラを
ストリーミングしてみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/fb01fb15-7edc-25f3-03d0-3797bac72b0e.png)


## 余談
この記事は12/6に書く予定でしたが、
書き終えたのは結局アドベントカレンダー最終日（汗）。
その間に、SORACOMさんがストリーミング再生し放題の機能を提供開始したと
プレスリリースがありました。

[ソラカメ、カメラのライブ映像を無制限で視聴・ダウンロードできる「ライブ視聴見放題」を提供開始](https://soracom.com/ja-jp/news/20231214-camera-livestreaming/)

ソラカメの活用がますますしやすくなりそうです。