普段macOSにEmacsをインストールし、それを起点にして様々な作業をしている。文書の編集作業もそうだけれど、シェルでの作業であったり、データベースの操作であったり、メールやタスクの管理であったり、作業の種類は多岐に渡る。
オーディオデバイスからの入力を、FFmpegを用いて録音しようとした所、オーディオデバイスからの入力を上手く受け取れず、空の音声ファイルが作られる事象に遭遇した。他の環境ではどうかと考え、ターミナル(Terminal.app)から実行してみるときちんと録音できた。どうやら権限の問題のような気がする。「セキュリティとプライバシー」の「マイク」を確認してみると「ターミナル」はあるが「Emacs」はない。今回はこの問題を何とかする事にした。なおEmacsは、ソースコードから自分でビルドしたものを使用している。もしかしたら、通常のインストール方法のEmacsでは、こんな事は起きないかもしれない。
TCC - Transparency Consent and Control
macOSにはTransparency Consent and Controlという機能がある。これはアプリケーションがユーザーに関連するデータにアクセスできるかを制御するための、セキュリティフレームワークだ。この値は通常「セキュリティとプライバシー」で表示され、GUIで切り替える事ができる。ただし「マイク」の所には特定のアプリケーションしか表示されていないし、アプリケーションを追加するような項目もない。
TCCについては詳しくは説明しない(詳しくないので説明できない)が、 ~/Library/Application Support/com.apple.TCC/TCC.db や /Library/Application Support/com.apple.TCC/TCC.db にSQLite形式のデータベースがあり、そこで権限の管理をしている1。一応中身は見たが、どうすれば良いのかよく分からなかった。
ちなみに access というテーブルがそれらしい値を管理しているようだった。テーブル定義は以下のようになっていた。
3CREATE TABLE access (
service TEXT NOT NULL,
client TEXT NOT NULL,
client_type INTEGER NOT NULL,
auth_value INTEGER NOT NULL,
auth_reason INTEGER NOT NULL,
auth_version INTEGER NOT NULL,
csreq BLOB,
policy_id INTEGER,
indirect_object_identifier_type INTEGER,
indirect_object_identifier TEXT NOT NULL DEFAULT 'UNUSED',
indirect_object_code_identity BLOB,
flags INTEGER,
last_modified INTEGER NOT NULL DEFAULT (CAST(strftime('%s','now') AS INTEGER)
),
PRIMARY KEY (service, client, client_type, indirect_object_identifier),
FOREIGN KEY (policy_id)
REFERENCES policies(id) ON DELETE CASCADE ON UPDATE CASCADE);
やったこと
必要な事はEmacsからマイクへのアクセスをTCCに許可させる事だ。どれが必要な事なのか分からなかったけれど、やった事を書く。
Info.plistにマイクへの権限を追記する
アプリケーションにマイクへのアクセス権限がないと、録音しようとしてもコマンドは失敗する。GUIからマイクへのアクセス権限を与える方法が分からなかったが、アプリケーションのディレクトリ(XXXX.appというディレクトリになっているはず)のContents配下にある Info.plist に、権限の宣言をする事で無理矢理権限を付与する事ができる。ここではEmacsにその権限を与えた例を示す。
〜省略〜
<key>NSMicrophoneUsageDescription</key>
<string>Emacs requires permission to access microphone.</string>
</dict>
</plist>NSMicrophoneUsageDescription がマイクへのアクセスを要求するキーらしい。
Emacsをコード署名する
codesignを使い、Emacsに付与された権限を確認する。
$ codesign -d --entitlements - /Applications/Emacs.app/
/Applications/Emacs.app/: code object is not signed at allすると code object is not signed at all と表示された。どうやらコード署名されていないらしい。確かにコード署名した記憶はない。そこでコード署名する事にした。
コード署名するには、署名用の証明書が必要になる。これは Apple Developer Program を登録していれば、そのための証明書を取得できるが、その変わりに年間99ドル支払う必要がある。
もし Apple Developer Program を登録していなければ、自己証明書を使う事もできる。この自己証明書は KeyChain Access の証明書アシスタントで作成できる。
- メニューから「証明書アシスタント」を起動する。
以下の情報を設定する。
- 名前
- 好きな名前
- 固有名のタイプ
- 自己署名ルート
- 証明書のタイプ
- コード署名
- 「作成」を押す。
作成した証明書を使い、Emacsをコード署名する。
codesign --deep -s "証明書の名前" /Applications/Emacs.appこれでコード署名できる。かなり横着したけれど --deep を使ったり、 /Applications配下にコピー済みのアプリケーショに対して署名するなどといった事はしない方がいいかもしれない。
コード署名後はアプリケーション(Emacs)を再起動する。
コード署名については、以前macOSにインストールしたプログラムをコード署名するにも記載した。
マイクにアクセスしTCCのダイアログでマイクへのアクセスを許可する
Emacsを再起動したらEmacsからマイクへアクセスする。マイクへのアクセ<ス方法は何でも良いけれど、ここではFFmpegを使う。
FFmpegでオーディオデバイスからの入力を取得するには、デバイス番号を指定する必要がある。デバイスの番号は ffmpeg -f avfoundation -list_devices true -i "" で確認できる。 -f avfoundation はmacOS上で行う時に必要なパラメータだ。
ffmpeg -f avfoundation -list_devices true -i ""ここではデバイス番号は 1 とする。デバイスの確認の時にも指定した -f avfoundation を指定して、録音する。
ffmpeg -f avfoundation -i ":1" output.wavマイクにアクセスすると、マイクへのアクセスを許可するかどうかを選択するためのダイアログが表示される。これに許可を選択する。すると無事マイクからの入力を取得できる。
「セキュリティとプライバシー」の「マイク」にも「Emacs」の表示がされる。
まとめ
macOS上で野良ビルドしたEmacsからマイクへのアクセスができなかった。Emacsの権限の設定を変更し、コード署名をする事でマイクからの入力を取得できるようになった。
このデータベースは、直接書き換えない方が良いらしい。