Asterisk で通話音声データを別アプリに転送する設定を調べた際のメモ

Asterisk13 の頃に行った調査ですが、今の版でもたぶん使えるハズ。。。

# 背景
* Asterisk はIP電話交換機のサーバーアプリケーション
* Asterisk は通話音声データをファイルに出力可能。


# 目的
* 通話が終わってファイル出力された後ではなく、通話中に音声データを自前のアプリケーションに転送したい。
    * 可能かどうかから調査し、手法を確立した。

# 調査結果概要
|項目|結果|補足|
|---|---|---|
|通話録音は可能か？|可能|
|話者別の録音は可能か？|可能|発信者と受信者を別ファイルで出力し、soxコマンド等での後処理で、ステレオ１ファイルにしたり、合成してモノラル１ファイルにすることも可能|
|音声フォーマットは？| wav(16bit-PCM, 8KHz)|mu-lawではなく 16bit-PCMに変換してくれている|
|通話開始時に外部処理は呼び出し可能か？|可能|シェルスクリプトが実行出来る事を確認した。シェルスクリプト実行中は通話開始処理がブロックされるので短時間で戻るスクリプトにすること|
|通話終了時に外部処理は呼び出し可能か？|可能| 同上|
|名前付きPIPEへの出力は可能か？|可能|Asteriskの設定で「追記オプション」を指定する必要あり|
|メタデータは取得可能か？| 可能|項目は後述|

* 名前付きPIPEを使って、Asteriskから通話音声を、別アプリに転送できる事が分かった。

# Asterisk から取得可能な 通話毎のメタ情報
* ↓の値は、extension.conf の中で使用できる。外部シェルスクリプトへの伝達も可能。

|項目|実験結果(空白は表示されなかった所)|補足|
|---|---|---|
|CDR(accountcode)||アカウントコード（指定されている場合）
|BLINDTRANSFER  |  |
|BRIDGEPEER  |  |
|BRIDGEPVTCALLID  |  |
|CALLERID(ani)  |204  |呼び出し側ANI（PRIチャネル）
|CALLERID(ani2)  |0  |ANI2（情報桁）は発信元情報またはOLIとも呼ばれる
|CALLERID(all)  |<204>  |発信者番号
|CALLERID(dnid)  |202  |ダイヤル番号識別子
|CALLERID(name)  |  |発信者番号のみ
|CALLERID(num)  |204  |発信者番号のみ
|CALLERID(rdnis)  |  |リダイレクトされたダイヤル番号IDサービス
|CALLINGANI2  |0  |発信者ANI2（PRIチャネル）
|CALLINGPRES  |0  |着信呼（PRIチャネル）の発信者番号表示
|CALLINGTNS  |0  |トランジットネットワークセレクタ（PRIチャネル）
|CALLINGTON  |0 |発信者番号タイプ（PRIチャネル）
|CHANNEL  |SIP/204-0000002a  |現在のチャンネル名
|CONTEXT  |default  |現在のコンテキスト
|DATETIME  |  |現在の日付時刻
|DB_RESULT  |  |ダイヤルプラン関数の結果値
|EPOCH  |1527680480  |現在のUNIXスタイルのエポック
|EXTEN  |202  |現在の拡張機能（内線番号）
|ENV(VAR)  |  |環境変数VAR
|GOTO_ON_BLINDXFR  |  |
|HANGUPCAUSE  |0  |ハングアップのアスタリスク原因
|HINT  |  |この拡張機能のチャンネルヒント
|HINTNAME  |  |この内線番号の推奨発信者* ID名
|INVALID_EXTEN  |  |無効な着信内線番号
|LANGUAGE  |  |現在の言語
|LEN(VAR)  |3  |VARの文字列の長さ（整数）
|PRIORITY  |32  |ダイヤルプランの現在の優先度
|PRIREDIRECTREASON  |  |コールが転送された場合のPRIのリダイレクトの理由
|TIMESTAMP  |  |現在の日付時刻
|TRANSFER_CONTEXT  |  |転送された呼び出しのコンテキスト
|FORWARD_CONTEXT  |  |FORWARD_CONTEXT
|DYNAMIC_PEERNAME|  |動的機能が使用されているときの相手側のチャネルの名前
|DYNAMIC_FEATURENAME  |  |最後にトリガーされた動的機能の名前
|UNIQUEID  |1527680480.62  |現在の呼び出し一意識別子
|SYSTEMNAME  |  |asterisk.confのsystemnameオプションの値
|ENTITYID  |08:00:27:ce:90:6d  |グローバルエンティティIDが自動的に設定されるか、asterisk.confから設定される
|AGIEXITONHANGUP  |  |
|CALENDAR_SUCCESS  |  |
|SIP_RECVADDR  |  |
|VOICEMAIL_PLAYBACKSTATUS  |  |
|SIPCALLID  |RXc5L17aw-DSeHZWVZJWfD.xGCboeJwR  |SIPコールID：ヘッダー形式（ロギングまたはCDRマッチング用）
|SIPDOMAIN  |172.16.3.220  |インバウンドコールのSIP宛先ドメイン（該当する場合）
|SIPFROMDOMAIN  |  |発信コールのFromヘッダーのSIPドメイン部分を設定する
|SIPUSERAGENT  |  |SIPユーザーエージェント（非推奨）
|SIPURI  |sip:204@172.16.1.64:5060  |SIP URI
|SIP_MAX_FORWARDS  |  |発信コールのMax-Forwardsヘッダーの値を設定します
|SIP_CODEC  |  |インバウンドコールのSIPコーデックを設定する


### 参照
- アスタリスク標準チャネル変数[https://wiki.asterisk.org/wiki/display/AST/Asterisk+Standard+Channel+Variables](https://wiki.asterisk.org/wiki/display/AST/Asterisk+Standard+Channel+Variables)
- chan_sipチャネル変数[https://wiki.asterisk.org/wiki/display/AST/chan_sip+Channel+Variables](https://wiki.asterisk.org/wiki/display/AST/chan_sip+Channel+Variables)

### 補足：実験環境

|   |発信者| IP-PBX| 受信者
|---|---|---|---|
|Sip番号|204| |202|
|IPアドレス|172.16.1.64|172.16.3.220|172.16.1.3|


# Asterisk 設定ファイル
|ファイル名|概要|
|---|---|
|asterisk.conf |ディレクトリの指定。設定ファイル、ログ出力先、シェルスクリプト格納先、Wav 出力先 、のディレクトリを指定する。 |
|extensions.conf |内線番号毎の着信時の処理をBASICライクのスクリプトで記述する。 |
|sip.conf |内線電話の番号(アカウント)とパスワード を記載する。 |

## /etc/asterisk/extensions.conf
- Asterisk の着信時の処理内容(ダイアルプラン)は、/etc/asterisk/extensions.conf に記述する。
- ダイアルプラン: https://wiki.asterisk.org/wiki/display/AST/Contexts%2C+Extensions%2C+and+Priorities
- extensions.conf の記述方法は BASIC っぽい

### extensions.conf のサンプル 
- 内線番号 201～209 充ての着信時に、外部シェルスクリプトを実行し、録音を開始する例。

```
;内線呼び出し
exten => _20Z,1,NoOp(内線呼出)
exten => _20Z,n,Set(Cid=--Cid="${SIPCALLID}")
exten => _20Z,n,Set(Lid=--Lid="sip:${EXTEN}")
exten => _20Z,n,Set(Rid=--Rid="${SIPURI}")
exten => _20Z,n,AGI(MyShellScript-CallOpen.sh,${Cid},${Lid},${Rid})
exten => _20Z,n,MixMonitor(,r(/dev/shm/${EXTEN}/R.wav)t(/dev/shm/${EXTEN}/L.wav)ab,/var/lib/asterisk/agi-bin/MyShellScript-CallClose.sh ${EXTEN} )
exten => _20Z,n,Dial(SIP/${EXTEN},60)
exten => _20Z,n,Hangup
```
* 行頭の”；” は コメント行を表す。
* 各行は↓の記述
    * `exten` => `内線番号` , `コマンド行番号` , `コマンド`　 
#### 内線番号
* 内線番号の指定にて、先頭文字が ”_" の時には パターンマッチ記述を示す。
    * 例）"_20Z" は 内線番号 201～209 を意味する。
* パターンマッチに利用できる文字を下表に示す。

| 文字| 意味|
|---|---|
|X|0～9 の数字にマッチ
|Z|1～9 の数字にマッチ
|N|2～9 の数字にマッチ
|[123-6]|[]内の文字/数字にマッチする
|.|「何にでも」マッチする
|!|0文字以上の何にでもマッチする


#### コマンド行番号
- 開始行に "1" は 必須。
- 次行以降は ”n" で 省略可能。
    - goto や gosub での 飛ばし先となる行には 行番号を指定する。

#### コマンド
- ダイアルプラン コマンド[https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Dialplan+Applications](https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Dialplan+Applications)
- ダイアルプラン 関数[https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Dialplan+Functions](https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Dialplan+Functions)
- サンプルで示したコマンドの説明

|コマンド名|概要|詳細へのLink
|---|---|---|
|NonOp()| いわゆるPRINT |https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Application_NoOp
|AGI()|シェルスクリプト等の外部コマンドを実行する。任意の引数を指定可能。|https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Application_AGI
|Set()|変数定義、メタデータの値を外部コマンドへの引数に用いる場合に使用。|https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Application_Set
|MixMonitor()|通話音声データをファイル出力する|https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Application_MixMonitor
|Dial()|内線電話の端末を呼び出す。|https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Application_Dial
|Hungup()|通話終了|https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Application_Hangup

##### AGI() コマンドの補足
- AGI( 実行ファイル名 , 引数... )
    - 引数は省略可能
    - 実行ファイル名にPATHが含まれてなければ、 /var/lib/asterisk/agi-bin/ を 探索する。
- AGI（）から戻るまでは Block される。
    - 外部コマンドは短時間で戻るものとすること。
- AGI（）で呼びだす外部コマンドに伝達するメタ情報は、AGI（）コマンドの引数で明示的に与えるものと、暗黙で伝達されるもの２種類がある。
    - 引数で明示的に与えられるメタ情報：
        - メタ情報が利用可能。
        - 外部コマンド側は引数として読み取る。
            - C言語であれば、main(int argc , char **argv ) の argc と argv から読み取る。
            - シェルスクリプトならば、＄1 ＄2 等で読み取る。
                
    - 暗黙で伝達されるメタ情報：
        - 外部プログラムへは、標準入力（stdin)を用いて伝達する。
        - ”項目名: 値” ＋改行 の 記述で、1行で１項目が伝達される。
        - 空行が引数の終端。
        - （HTTPのヘッダに似ている）
        - 暗黙で伝達されるメタ情報の一覧を下表に示す。

|項目名|実験時に受けた値|
|---|---|
|agi_channel: |SIP/204-00000000 |
|agi_language: |ja |
|agi_type: |SIP |
|agi_uniqueid: |1527841359.0 |
|agi_version: |13.20.0 |
|agi_callerid: |204 |
|agi_calleridname: |unknown |
|agi_callingpres: |0 |
|agi_callingani2: |0 |
|agi_callington: |0 |
|agi_callingtns: |0 |
|agi_dnid: |202 |
|agi_rdnis: |unknown |
|agi_context: |default |
|agi_extension: |202 |
|agi_priority: |63|
|agi_enhanced: |0.0 |
|agi_accountcode: | |
|agi_threadid: |140425977038592 |

##### MixMonitor()コマンド補足  話者別に名前付きPIPEに出力する指定
- MixCommand( Mix済み出力ファイル名 , オプション, 録音終了時実行コマンド）
    - Mix済み出力ファイルの指定があれば、両端末の音声を合成しモノラルのwavファイルを出力する。
        - 省略可能。省略時にはファイルはMix済み出力ファイルは出力されない。
    - 録音終了時実行コマンド は (例１)に示す記述で、引数付きでシェルスクリプトを呼びだせる。
    - 話者別にPIPE出力を行う オプションの意味を下表に示す。
 
|引数名| 概要|
|---|---|
|r("ファイル名") |発信者側 通話音声の出力先ファイル名 |
|t("ファイル名") |受信者側 通話音声の出力先ファイル名 |
|a |追記指定：名前付きPIPEへの追記が可能(この指定を行わないと通常ファイルで作り直される) |
|b |通話開始時から録音開始|

# 付録：Asterisk コンソールコマンド
- Asteriskのコンソールの起動方
```
    > asterisk -vvvcr
```
- AGI 出力のデバッグ出力の 有効化
```
    > agi set debug on
```
- 設定ファイルの再読み込み
```
    > core reload
```
- extensions.confの再読み込み
```
    > dialplan reload
```

# 参考URL
- ダイアルプランの概要[https://wiki.asterisk.org/wiki/display/AST/Contexts%2C+Extensions%2C+and+Priorities](https://wiki.asterisk.org/wiki/display/AST/Contexts%2C+Extensions%2C+and+Priorities)
- ダイアルプラン コマンド[https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Dialplan+Applications](https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Dialplan+Applications)
- ダイアルプラン 関数[https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Dialplan+Functions](https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Dialplan+Functions)
- AGI コマンド[https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+AGI+Commands](https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+AGI+Commands)
- ダイアルプラン 変数[https://wiki.asterisk.org/wiki/display/AST/Variables](https://wiki.asterisk.org/wiki/display/AST/Variables)
- 音声ファイル出力 用 ダイアルプラン コマンド : Monitor() PIPE 出力不可[https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Application_Monitor](https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Application_Monitor)
- 音声ファイル出力 用 ダイアルプラン コマンド : MixMonitor() LR分け PIPE 出力可能[https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Application_MixMonitor](https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Application_MixMonitor)
    - 終了時に 外部ファイルが呼び出せるか？　は調査中
    - 録音ファイルと、L,R ファイルの別に録音 の例[https://stackoverflow.com/questions/34288644/can-asterisk-monitor-and-mixmonitor-applications-be-executed-on-same-channel](https://stackoverflow.com/questions/34288644/can-asterisk-monitor-and-mixmonitor-applications-be-executed-on-same-channel)
- アスタリスクゲートウェイインターフェイス（AGI）[http://www.asteriskdocs.org/en/3rd_Edition/asterisk-book-html-chunk/AGI.html](http://www.asteriskdocs.org/en/3rd_Edition/asterisk-book-html-chunk/AGI.html)[http://www.asteriskdocs.org/en/3rd_Edition/asterisk-book-html-chunk/AGI-variants.html](http://www.asteriskdocs.org/en/3rd_Edition/asterisk-book-html-chunk/AGI-variants.html)