REST APIでWowza Streaming EngineのApplicationを操作してみたよ

#はじめに
こんにちは。ブームにやや乗り遅れましたが呪術廻戦にハマってしまったstreampackのfadoです。華金は楽しみがいっぱいです。

さて、今まで`Wowza Streaming Engine`を使って作業している時は、管理画面(`Wowza Streaming Engine Manager`)にログインし、ポチポチしていました。それだと確かにGUIは楽なのですがAPIでの制御を必要とする業務があり、慌てて色々調べ始めると言ったことがありました。そこで今回の記事では、その経験を踏まえて、自分なりに理解したApplicationの作成やプロパティの追加などの作業について簡単に解説していきたいと思います。ちなみにApplicationとはメデイアストリーミングに関する様々な制御、プロパティを定義する、`Wowza Streaming Engine`において核となる部分です。作成する画面はこのような感じですね。

![スクリーンショット 2021-02-19 3.52.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429912/b29e48c7-c2e9-746e-4c0d-7023e94ac558.png)

#注意事項
* `Wowza Streaming Engine`(以下`Wowza`)
 * サーバーは`AWS`環境で構築したもの
 * バージョン4.8.9 (build 20201221171606)
 * 認証方法はbasicです
* `Wowza`についてある程度知見のある方々が対象です。
* `Wowza`の`REST API`インターフェースはデフォルトで8087ポート/tcpを利用しているため、SGやFirewallを予め開放して下さい。

#手順
##Applicationを作成
Application Name(appName): livetest
Application Type: Live HTTP Origin
Server Name(serverName): \_defaultServer\_
VHost Name(vhostName): \_defaultVHost\_

* メソード
 * POST
* パス
 * /v2/servers/{serverName}/vhosts/{vhostName}/applications/{appName}

* リクエスト

```
$ curl -u "xxxx:yyyy" -X POST -H 'Accept:application/json; charset=utf-8' -H 'Content-Type:application/json; charset=utf-8'  http://XXX.XXX.XXX.XXX:8087/v2/servers/_serverName_/vhosts/_defaultVHost_/applications -d @livetest.json
```

* レスポンス

```
{"success":true,"message":"Application (livetest) created successfully.","data":null}
```

```json:livetest.json
{
  "name": "livetest",
  "version": "",
  "serverName": "_defaultServer_",
  "appType": "LiveHTTPOrigin",
  "applicationTimeout": 0,
  "pingTimeout": 0,
  "clientStreamReadAccess": "*",
  "clientStreamWriteAccess": "*",
  "avSyncMethod": "senderreport",
  "maxRTCPWaitTime": 12000,
  "httpCORSHeadersEnabled": true,
  "httpStreamers": [
    "sanjosestreaming",
    "cupertinostreaming",
    "smoothstreaming",
    "mpegdashstreaming"
  ],
  "mediaReaderRandomAccessReaderClass": "",
  "httpOptimizeFileReads": false,
  "mediaReaderBufferSeekIO": false,
  "captionLiveIngestType": "",
  "vodTimedTextProviders": [
    "vodcaptionprovidermp4_3gpp"
  ],
  "securityConfig": {
    "serverName": "_defaultServer_",
    "secureTokenVersion": 0,
    "clientStreamWriteAccess": "*",
    "publishRequirePassword": true,
    "publishPasswordFile": "",
    "publishRTMPSecureURL": "",
    "publishIPBlackList": "",
    "publishIPWhiteList": "",
    "publishBlockDuplicateStreamNames": false,
    "publishValidEncoders": "",
    "publishAuthenticationMethod": "digest",
    "playMaximumConnections": 0,
    "playRequireSecureConnection": false,
    "secureTokenSharedSecret": "",
    "secureTokenUseTEAForRTMP": false,
    "secureTokenIncludeClientIPInHash": false,
    "secureTokenHashAlgorithm": "",
    "secureTokenQueryParametersPrefix": "",
    "secureTokenOriginSharedSecret": "",
    "playIPBlackList": "",
    "playIPWhiteList": "",
    "playAuthenticationMethod": "none"
  },
  "streamConfig": {
    "serverName": "_defaultServer_",
    "streamType": "live",
    "storageDir": "${com.wowza.wms.context.VHostConfigHome}/content",
    "createStorageDir": false,
    "storageDirExists": true,
    "keyDir": "${com.wowza.wms.context.VHostConfigHome}/keys",
    "liveStreamPacketizer": [
      "cupertinostreamingpacketizer",
      "mpegdashstreamingpacketizer",
      "sanjosestreamingpacketizer",
      "smoothstreamingpacketizer"
    ],
    "httpRandomizeMediaName": true
  },
  "dvrConfig": {
    "serverName": "_defaultServer_",
    "licenseType": "Subscription",
    "inUse": false,
    "dvrEnable": false,
    "windowDuration": 0,
    "storageDir": "${com.wowza.wms.context.VHostConfigHome}/dvr",
    "archiveStrategy": "append",
    "dvrOnlyStreaming": false,
    "startRecordingOnStartup": true,
    "dvrEncryptionSharedSecret": "",
    "dvrMediaCacheEnabled": false,
    "httpRandomizeMediaName": false
  },
  "drmConfig": {
    "serverName": "_defaultServer_",
    "licenseType": "Subscription",
    "inUse": false,
    "ezDRMUsername": "",
    "ezDRMPassword": "",
    "buyDRMUserKey": "",
    "buyDRMProtectSmoothStreaming": false,
    "buyDRMProtectCupertinoStreaming": false,
    "buyDRMProtectMpegDashStreaming": false,
    "verimatrixProtectCupertinoStreaming": false,
    "verimatrixCupertinoKeyServerIpAddress": "",
    "verimatrixCupertinoKeyServerPort": 0,
    "verimatrixCupertinoVODPerSessionKeys": false,
    "verimatrixProtectSmoothStreaming": false,
    "verimatrixSmoothKeyServerIpAddress": "",
    "verimatrixSmoothKeyServerPort": 0,
    "cupertinoEncryptionAPIBased": false
  },
  "transcoderConfig": {
    "serverName": "_defaultServer_",
    "available": true,
    "licensed": true,
    "licenses": -1,
    "licensesInUse": 0,
    "templates": {
      "vhostName": "_defaultVHost_",
      "templates": [
        {
          "id": "audioonly",
          "href": "/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/livetest/transcoder/templates/audioonly"
        },
        {
          "id": "transcode-h265",
          "href": "/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/livetest/transcoder/templates/transcode-h265"
        },
        {
          "id": "transcode",
          "href": "/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/livetest/transcoder/templates/transcode"
        },
        {
          "id": "transrate",
          "href": "/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/livetest/transcoder/templates/transrate"
        }
      ]
    },
    "templatesInUse": "${SourceStreamName}.xml,transrate.xml",
    "profileDir": "${com.wowza.wms.context.VHostConfigHome}/transcoder/profiles",
    "templateDir": "${com.wowza.wms.context.VHostConfigHome}/transcoder/templates",
    "createTemplateDir": false
  },
  "webRTCConfig": {
    "serverName": "_defaultServer_",
    "enablePublish": false,
    "enablePlay": false,
    "enableQuery": false,
    "iceCandidateIpAddresses": "127.0.0.1,tcp,1935",
    "preferredCodecsAudio": "opus,pcmu,pcma",
    "preferredCodecsVideo": "vp8,h264",
    "debugLog": false
  },
  "modules": {
    "serverName": "_defaultServer_",
    "moduleList": [
      {
        "description": "Base",
        "name": "base",
        "order": 0,
        "class": "com.wowza.wms.module.ModuleCore"
      },
      {
        "description": "Client Logging",
        "name": "logging",
        "order": 1,
        "class": "com.wowza.wms.module.ModuleClientLogging"
      },
      {
        "description": "FLVPlayback",
        "name": "flvplayback",
        "order": 2,
        "class": "com.wowza.wms.module.ModuleFLVPlayback"
      },
      {
        "description": "Core Security Module for Applications",
        "name": "ModuleCoreSecurity",
        "order": 3,
        "class": "com.wowza.wms.security.ModuleCoreSecurity"
      },
      {
        "description": "Schedules streams and playlists.",
        "name": "streamPublisher",
        "order": 4,
        "class": "com.wowza.wms.plugin.streampublisher.ModuleStreamPublisher"
      }
    ]
  }
}
```
##作成したApplicationの情報を取得

* メソッド
 * GET
* パス
 * /v2/servers/{serverName}/vhosts/{vhostName}/applications/{appName}

* リクエスト

```
curl -u "xxxx:yyyy" -X GET -H 'Accept:application/json; charset=utf-8' -H 'Content-Type:application/json; charset=utf-8'  http://XXX.XXX.XXX.XXX:8087/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/livetest
```

* レスポンス

```
{"name":"livetest","version":"1613666179000","serverName":"_defaultServer_","appType":"LiveHTTPOrigin","applicationTimeout":0,"pingTimeout":0,"clientStreamReadAccess":"*","clientStreamWriteAccess":"*","avSyncMethod":"senderreport","maxRTCPWaitTime":12000,"httpCORSHeadersEnabled":true,"httpStreamers":["sanjosestreaming","cupertinostreaming","smoothstreaming","mpegdashstreaming"],"mediaReaderRandomAccessReaderClass":"","httpOptimizeFileReads":false,"mediaReaderBufferSeekIO":false,"captionLiveIngestType":"","vodTimedTextProviders":["vodcaptionprovidermp4_3gpp"],"securityConfig":{"serverName":"_defaultServer_","secureTokenVersion":0,"clientStreamWriteAccess":"*","publishRequirePassword":true,"publishPasswordFile":"","publishRTMPSecureURL":"","publishIPBlackList":"","publishIPWhiteList":"","publishBlockDuplicateStreamNames":false,"publishValidEncoders":"","publishAuthenticationMethod":"digest","playMaximumConnections":0,"playRequireSecureConnection":false,"secureTokenSharedSecret":"","secureTokenUseTEAForRTMP":false,"secureTokenIncludeClientIPInHash":false,"secureTokenHashAlgorithm":"","secureTokenQueryParametersPrefix":"","secureTokenOriginSharedSecret":"","playIPBlackList":"","playIPWhiteList":"","playAuthenticationMethod":"none"},"streamConfig":{"serverName":"_defaultServer_","streamType":"live","storageDir":"${com.wowza.wms.context.VHostConfigHome}/content","createStorageDir":false,"storageDirExists":true,"keyDir":"${com.wowza.wms.context.VHostConfigHome}/keys","liveStreamPacketizer":["cupertinostreamingpacketizer","mpegdashstreamingpacketizer","sanjosestreamingpacketizer","smoothstreamingpacketizer"],"httpRandomizeMediaName":true},"dvrConfig":{"serverName":"_defaultServer_","licenseType":"Subscription","inUse":false,"dvrEnable":false,"windowDuration":0,"storageDir":"${com.wowza.wms.context.VHostConfigHome}/dvr","archiveStrategy":"append","dvrOnlyStreaming":false,"startRecordingOnStartup":true,"dvrEncryptionSharedSecret":"","dvrMediaCacheEnabled":false,"httpRandomizeMediaName":false},"drmConfig":{"serverName":"_defaultServer_","licenseType":"Subscription","inUse":false,"ezDRMUsername":"","ezDRMPassword":"","buyDRMUserKey":"","buyDRMProtectSmoothStreaming":false,"buyDRMProtectCupertinoStreaming":false,"buyDRMProtectMpegDashStreaming":false,"verimatrixProtectCupertinoStreaming":false,"verimatrixCupertinoKeyServerIpAddress":"","verimatrixCupertinoKeyServerPort":0,"verimatrixCupertinoVODPerSessionKeys":false,"verimatrixProtectSmoothStreaming":false,"verimatrixSmoothKeyServerIpAddress":"","verimatrixSmoothKeyServerPort":0,"cupertinoEncryptionAPIBased":false},"transcoderConfig":{"serverName":"_defaultServer_","available":true,"licensed":true,"licenses":-1,"licensesInUse":0,"templates":{"vhostName":"_defaultVHost_","templates":[{"id":"audioonly","href":"/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/livetest/transcoder/templates/audioonly"},{"id":"transcode-h265","href":"/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/livetest/transcoder/templates/transcode-h265"},{"id":"transcode","href":"/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/livetest/transcoder/templates/transcode"},{"id":"transrate","href":"/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/livetest/transcoder/templates/transrate"},{"id":"mnetmulti-4k","href":"/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/livetest/transcoder/templates/mnetmulti-4k"}]},"templatesInUse":"${SourceStreamName}.xml,transrate.xml","profileDir":"${com.wowza.wms.context.VHostConfigHome}/transcoder/profiles","templateDir":"${com.wowza.wms.context.VHostConfigHome}/transcoder/templates","createTemplateDir":false},"webRTCConfig":{"serverName":"_defaultServer_","enablePublish":false,"enablePlay":false,"enableQuery":false,"iceCandidateIpAddresses":"127.0.0.1,tcp,1935","preferredCodecsAudio":"opus,pcmu,pcma","preferredCodecsVideo":"vp8,h264","debugLog":false},"modules":{"serverName":"_defaultServer_","moduleList":[{"description":"Base","name":"base","order":0,"class":"com.wowza.wms.module.ModuleCore"},{"description":"Client Logging","name":"logging","order":1,"class":"com.wowza.wms.module.ModuleClientLogging"},{"description":"FLVPlayback","name":"flvplayback","order":2,"class":"com.wowza.wms.module.ModuleFLVPlayback"},{"description":"Core Security Module for Applications","name":"ModuleCoreSecurity","order":3,"class":"com.wowza.wms.security.ModuleCoreSecurity"},{"description":"Schedules streams and playlists.","name":"streamPublisher","order":4,"class":"com.wowza.wms.plugin.streampublisher.ModuleStreamPublisher"}]}}
```

このままだとレスポンスが1行に表示され、情報が多すぎて何が何だかわかりづらいのでいつもはjsonlintで見やすい形に整形させています。jsonlintはJSON構文のチェックにも役立ちます。

```
$ curl -s -u "xxxx:yyyy" -X GET -H 'Accept:application/json; charset=utf-8' -H 'Content-Type:application/json; charset=utf-8'  http://XXX.XXX.XXX.XXX:8087/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/livetest | jsonlint
```
■ Mac環境では`brew install jsonlint`
■ npmが使える環境では `npm install jsonlint -g`
でインストール可能です。

##Applicationの情報を更新
* 例ではsmoothstreamingのパケタイザを削除します。
* メソッド
 * PUT
* パス
 * /v2/servers/{serverName}/vhosts/{vhostName}/applications/{appName}

* リクエスト

```
$ curl -u "xxxx:yyyy" -X PUT -H 'Accept:application/json; charset=utf-8' -H 'Content-Type:application/json; charset=utf-8'  http://XXX.XXX.XXX.XXX:8087/v2/servers/_serverName_/vhosts/_defaultVHost_/applications/livetest -d @livetest-update.json
```

* レスポンス

```
{"success":true,"message":"Saved","data":null}
```

```json:livetest-update.json
{
  "name": "livetest",
  "version": "",
  "serverName": "_defaultServer_",
  "appType": "LiveHTTPOrigin",
  "maxRTCPWaitTime": 12000,
  "httpCORSHeadersEnabled": true,
  "streamConfig": {
    "serverName": "_defaultServer_",
    "streamType": "live",
    "storageDir": "${com.wowza.wms.context.VHostConfigHome}/content",
    "createStorageDir": false,
    "storageDirExists": true,
    "keyDir": "${com.wowza.wms.context.VHostConfigHome}/keys",
    "liveStreamPacketizer": [
      "cupertinostreamingpacketizer",
      "mpegdashstreamingpacketizer",
      "sanjosestreamingpacketizer"
    ],
    "httpRandomizeMediaName": true
  }
}
```
##Applicationに詳細設定を追加
* 例ではHLSのchunkプロパティの設定を有効にし、値を変更します。
    * cupertinoChunkDurationTarget
     * 6000
 * cupertinoMaxChunkCount
     * 20
 * cupertinoPlaylistChunkCount 
     * 6
* メソッド
 * PUT
* パス
 * /v2/servers/{serverName}/vhosts/{vhostName}/applications/{appName}/adv

* リクエスト:

```
$ curl -u "xxxx:yyyy" -X PUT -H 'Accept:application/json; charset=utf-8' -H 'Content-Type:application/json; charset=utf-8'  http://XXX.XXX.XXX.XXX:8087/v2/servers/_serverName_/vhosts/_defaultVHost_/applications/livetest/adv -d @livetest-adv.json
```
* レスポンス:

```
{"success":true,"message":"Saved","data":null}
```

```json:livetest-adv.json
{
  "version": "",
  "serverName": "_defaultServer_",
  "advancedSettings": [
    {
      "enabled": true,
      "canRemove": true,
      "name": "cupertinoChunkDurationTarget",
      "value": "6000",
      "defaultValue": "10000",
      "type": "Integer",
      "sectionName": "cupertinostreamingpacketizer",
      "section": "/Root/Application/LiveStreamPacketizer",
      "documented": true
    },
    {
      "enabled": true,
      "canRemove": true,
      "name": "cupertinoMaxChunkCount",
      "value": "20",
      "defaultValue": "10",
      "type": "Integer",
      "sectionName": "cupertinostreamingpacketizer",
      "section": "/Root/Application/LiveStreamPacketizer",
      "documented": true
    },
    {
      "enabled": true,
      "canRemove": true,
      "name": "cupertinoPlaylistChunkCount",
      "value": "6",
      "defaultValue": "9",
      "type": "Integer",
      "sectionName": "cupertinostreamingpacketizer",
      "section": "/Root/Application/LiveStreamPacketizer",
      "documented": true
    }
  ]
}
```
##Applicationの詳細情報を取得
* メソッド
 * GET
* パス
 * /v2/servers/{serverName}/vhosts/{vhostName}/applications/{appName}/adv 

* リクエスト

```
$ curl -s -u "xxxx:yyyy" -X GET -H 'Accept:application/json; charset=utf-8' -H 'Content-Type:application/json; charset=utf-8'  http://XXX.XXX.XXX.XXX:8087/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/livetest/adv |jsonlint
```

* レスポンス

**＊設定していない他のプロパティも取得しますのでかなり長い表示になります。**

```
{
  "version": "1613667662000",
  "serverName": "_defaultServer_",
  "advancedSettings": [
    {
      "enabled": false,
      "canRemove": true,
      "name": "debugAACTimecodes",
      "value": "false",
      "defaultValue": "false",
      "type": "Boolean",
      "sectionName": "cupertinostreamingpacketizer",
      "section": "/Root/Application/LiveStreamPacketizer",
      "documented": true
    },
    {
      "enabled": false,
      "canRemove": true,
      "name": "debugMP3Timecodes",
      "value": "false",
      "defaultValue": "false",
      "type": "Boolean",
      "sectionName": "cupertinostreamingpacketizer",
      "section": "/Root/Application/LiveStreamPacketizer",
      "documented": true
    },

〜省略〜
```

##Applicationを再起動
Applicationのプロパティ等を追加・更新した場合、Applicationの再起動が必要になります。

* メソッド
 * PUT 
* パス
 * /v2/servers/{serverName}/vhosts/{vhostName}/applications/{appName}/actions/{action} 

* リクエスト

```
$ curl -u "xxxx:yyyy" -X PUT -H 'Accept:application/json; charset=utf-8' -H 'Content-Type:application/json; charset=utf-8'  http://XXX.XXX.XXX.XXX:8087/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/livetest/actions/restart
```

* レスポンス

```
{"success":true,"message":"Application (livetest) shutdown","data":null}
```
##該当VHost配下に作成されたApplicationをリスト
* メソッド
 * GET
* パス
 * /v2/servers/{serverName}/vhosts/{vhostName}/applications
* リクエスト

```
$ curl -s -u "xxxx:yyyy" -X GET -H 'Accept:application/json; charset=utf-8' -H 'Content-Type:application/json; charset=utf-8'  http://XXX.XXX.XXX.XXX:8087/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications | jsonlint
```

* レスポンス

```
{
  "serverName": "_defaultServer_",
  "applications": [
    {
      "id": "live",
      "href": "/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/live",
      "appType": "Live",
      "dvrEnabled": false,
      "drmEnabled": false,
      "transcoderEnabled": false,
      "streamTargetsEnabled": false
    },
    {
      "id": "livetest",
      "href": "/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/livetest",
      "appType": "LiveHTTPOrigin",
      "dvrEnabled": false,
      "drmEnabled": false,
      "transcoderEnabled": false,
      "streamTargetsEnabled": false
    },
    {
      "id": "vod",
      "href": "/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/vod",
      "appType": "VOD",
      "dvrEnabled": false,
      "drmEnabled": false,
      "transcoderEnabled": false,
      "streamTargetsEnabled": false
    }
  ]
}
```
##Applicationを削除
* メソッド
 * DELETE
* パス
 * /v2/servers/{serverName}/vhosts/{vhostName}/applications/{appName}
* リクエスト

```
$ curl -u "xxxx:yyyy" -X DELETE -H 'Accept:application/json; charset=utf-8' -H 'Content-Type:application/json; charset=utf-8'  http://XXX.XXX.XXX.XXX:8087/v2/servers/_defaultServer_/vhosts/_defaultVHost_/applications/livetest
```

* レスポンス

```
{"success":true,"message":"Application (livetest) deleted successfully","data":null}
```

#まとめ
如何でしょうか。Applicationの作成だけではなく、`Stream Files`や`SMIL Files`の作成・削除、Server Statusの取得、Serverチューニングまで簡単に制御できます。`API`を活用すれば`Amazon API Gateway`や`AWS Lambda`との連携も可能になり、あらゆる場面で細かいオペレーションまで行うことができます。機会があれば他の設定も紹介していけたらと思います。最後までお読みいただきありがとうございました！

#参考文献
https://www.wowza.com/docs/application-management-query-examples