azukipochette's weblog

memory dump (mini)

ffmpeg で動画を変換して 1TB 節約した話

f:id:AzukiPochette:20200224052326j:plain unsplash-logoCeline Nadon

はじめに

私は 2018 年に発売された mac mini を遊び用途で使っています。

写真や動画の保存にも使っているのですが、画像や動画は "容量食い" が激しいため、保存場所を考え直すことに *1。写真のほとんどは、Cloud Storage や Photo Services に移動させましたが、動画だけは良い移行先を見つけられず、オフラインで保存することにしました。

そこで、USB 3.0 で接続できる 4TB の大容量 HDD を購入し、mac mini に接続して使っていました。が... 約半年、あっという間に容量が枯渇しました。(「大容量だから」と気にせず保存していたのが主敗因です...)

HDD の追加購入も考えましたが、そもそも 4TB の HDD を半年ぐらいで使い方したことを考えると、この先、家の片隅に HDD が山積みされて、消費電力が心配になる未来が見えたため、追加購入を考える前に、さすがに何か "工夫" をしようと考えます。

対策: ffmpeg で動画を変換しよう

容量削減の方法として、動画の解像度を下げて変換することにしました。

これまでは、視聴時には 4K モニターを使うため、なんとなく 4K に耐えうる高画質で保存していましたが、それだと 1 ファイルあたり 6 - 7 GB あるため、解像度を下げて 2 GB 以下を目指します。

100 ファイル以上あるファイルを変換することになるため、スクリプトで扱うことができる(コマンドラインで実行できる)、"ffmpeg" を選択することにしました。

ffmpeg を mac に導入する方法は、Homebrew を使うと非常に簡単です。Homebrew は、mac OS 向けのパッケージ マネージャーで macOS の開発者界隈では広く使われています。インストール方法などについてはこの記事で詳しく扱いませんが、下記を見れば簡単に導入できます。

brew.sh

Homebrew で ffmpeg をインストールするには、以下のコマンドを実行します。

brew install ffmpeg

画質の考察

動画を変換する上で最も重要なことは「どういった画質として保存するか」であることは言うまでもありません。基本的に、画質が良ければ自然と大きくなり、小さくしようとすると画質は悪くなるものです。

元の動画の画質を知るには ffprobe コマンドを使用します。 ffprobe も ffmpeg も単純に実行すると、バナーと呼ばれる情報が表示されてしまうので、以下のように -hide_banner オプションを付与することをオススメします。

ffprobe -hide_banner /path/to/sample.avi

残念ながら「良い感じ最適化」といった曖昧な指定はできないため、動画素人な私はどの画質にするのかを一番悩みました。

結局、世界最大の動画サイトである YouTube の解像度やビットレートを参考にすることにしました。

support.google.com

簡単に表にまとめると、以下のようになります (30fps の場合)。

名称 横(px) 縦(px) 動画ビットレート範囲
4K (2160p) 3840 2160 13,000 ~ 34,000 Kbps
2K (1440p) 2560 1440 6,000 ~ 13,000 Kbps
FHD (1080p) 1280 720 3,000 ~ 6,000 Kbps
HD (720p) 1280 720 1,500 ~ 4,000 Kbps
(480p) 720 1280 500 ~ 2,000 Kbps
(360p) 640 360 400 ~ 1,000 Kbps

補足 : 480p は日本の DVD ビデオの製品規格で使われる解像度です。

上記を参考にいろいろと実験したところ、HD 画質まで落とせば、目標値である 2GB 程度まで下げられることがわかりました。

※ 当然ですが、どれが最適な品質というのは人それぞれだと思いますので、ご自身で数ファイル実験してみてください。

コーデックを選ぶ

次に、コーデックを選ぶことなりますが、対応しているコーデックは環境によって異なります。 サポートしているコーデックの一覧を取得するには以下のコマンドを実行します。

ffmpeg -hide_banner -codecs

実行すると、対応しているコーデックの一覧が表示されます。

近年、代表的な動画のフォーマットといえば "H.264"、"H.265/HEVC" ではないかと思います。

H.265 は、その名が示すとおり H.264 の後継規格で、60 fps をサポートしており、同画質を半分程度のサイズで実現するという今回の期待にぴったりのフォーマットです。H.264 と H.265 の違いはインターネット上に大量に記事がありますので、そちらを参考にしてください。

個人的には、富士通研究所の以下の記事がわかりやすかったです。

www.fujitsu.com

しかし、実験してみると変換が非常に遅く、時間がかかるため今回は "H.264" を選択することにしました (私の環境では、約 10 倍ほど H.264 のほうが高速でした)。*2

H.264 に決めたら、コーデック一覧から H.264 を探します。私の環境では以下のように表示されています。

 DEV.LS h264                 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (encoders: libx264 libx264rgb h264_videotoolbox )

見ると、"encoders" のところに 3 つ表示されていることがわかります。簡単に解説すると、以下の通りです。

エンコーダー 概要
libx264 オープンソースのソフトウェア エンコーダー
libx264rgb YUV の代わりに RGB を出力する libx264 のパッチ バージョン。
h264_videotoolbox Apple が提供している H.264 向けのハードウェア エンコーダー

(おまけ) libx264 で mac mini アツアツ事件

当初、"libx264" で実験していただのですが、エンコードを開始する度に mac mini からけたたましい音が聞こえ、本体の天板を触ってみると非常に熱い。おもむろにアクテビティ モニタを開くと、CPU 負荷が最大に。しかも、%GPU は 0% のまま。「...もしかして、ハードウェア エンコードに対応してないの!?」と思って調べてみると、mac mini (2018) には T2 チップが搭載されており、h264_videotoolbox を使うことでハードウェア エンコードができることを知りました。

ffmpeg を実行する

画質とフォーマット/エンコーダーを決めたら、いよいよ ffmpeg で動画を変換します。

私の場合は、HD (1280x720) サイズで、ビットレートは 1,500Kbps、h264_videotoolbox を使ってエンコードをしたいので、以下のように実行しました。

ffmpeg -i SAMPLE.mp4 -s 1280x720 -c:v h264_videotoolbox -b:v 1500k SAMPLE_HD.mp4

変換を自動化する

手作業ですべてのファイルを変換するのは大変なので、スクリプトを書いて自動化します。私の場合は、PowerShell (pwsh) を使ってスクリプトを書きました。

下記のスクリプトでは、現在のフォルダー配下のファイルを再帰的に取得し、ffmpeg で変換、変換に成功したら元ファイルを消しています。 ファイル名は HD で変換したことがわかるように、"_HD" を付与するようにしています。

$files = Get-ChildItem -Recurse -File
foreach ($file in $files)
{
    $newFullName = Join-Path -Path $item.DirectoryName -ChildPath ($item.BaseName + "_HD" + $item.Extension)
    $process = Start-Process -FilePath ffmpeg -ArgumentList "-i $($item.FullName) -s 1280x720 -c:v h264_videotoolbox -b:v 1500k $newFullName" -Wait
    if ($process.ExitCode -ne 0) {
        Remove-Item $item
    }
}

(おまけ) タグを使って除外する

全部のファイルを変換する場合は上記でいいのですが、一部の大切なファイルだけは除外したいという状況になりました。簡単にやるならば、フォルダー名やファイル名に除外を示す文字 (-,_,@ など) を付けてもいいのですが、名前を変えると名前順に並ばないなどの問題があるため、タグを使って除外することにしました。

finder のタグは拡張属性として保持されており、次のコマンドで取得できます。

xattr /path/to/yourfolder

タグが付与されている場合は、以下のような拡張属性が表示されます。

com.apple.FinderInfo
com.apple.metadata:_kMDItemUserTags

タグの情報は "com.apple.FinderInfo" に入っています。取得例を以下に示します。

☁  ~  xattr -p -x com.apple.FinderInfo /path/to/yourfolder
00 00 00 00 00 00 00 00 00 0C 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

10 バイト目に "0x0C" という値が見えますが、これが赤色をタグを示しています。

実験した結果を以下に示します。

灰 (GREY) 0x02
緑 (GREEN) 0x04
紫 (PURPLE) 0x06
青 (BLUE) 0x08
黄 (YELLOW) 0x0A
赤 (RED) 0x0C
橙 (ORANGE) 0x0E

実験の結果、タグを消すと拡張属性自体が消えるので、中身は見ずに拡張属性があるかどうかで判断することにしました。拡張属性は、タグ以外でも様々なツール (Dropbox etc) で利用されているため、ご自身の環境で調べてみてください。

残念ながら、PowerShell では拡張属性は読み取れないため、xattr の出力結果を使ってタグがあれば除外することにします。

コード例を以下に示します。

$dirs = Get-ChildItem -Directory
foreach ($dir in $dirs) {
    $attr =  xattr $dir.FullName
    if ($attr -eq $null) {
        # タグなし
    } else {
        # タグあり
    }
}

それでは、よい ffmpeg 生活を。Enjoy!

*1:mac mini に搭載されている SSD は 256GB です

*2:当初、最近話題の AV1 (AOMedia Video 1) で変換しようかと思っていましたが、見事に QuickTime が対応しておらず、メジャーなフォーマットを選ぶことにしました。

開発者のための Windows Terminal 入門

f:id:AzukiPochette:20200215000346j:plain

Windows Terminal は Windows 10 以降で使える新しいターミナルである。 従来のターミナル (cmd.exe など) と比較すると、"tmux" のようにタブやペインが使えるほか、ウィンドウ内での文字列検索などにも対応おり、絵文字 (🍺) の表示もサポートしている。

Windows ユーザーの多くは絵文字表示に必要性/利便性を感じないかもしれないが、Linux などの世界では出力に絵文字を使うアプリケーションも増えているため、Windows Subsystem for Linux (WSL) ユーザーなどは不満を感じていた点ではないかと思う。 ちなみに、 Windows の世界でも "Dapr" などマルチ プラットフォーム対応なアプリケーションの一部で絵文字が使われ始めている。

Windows Terminal のインストール

インストール要件

  • Windows 10 Version 1903 (18362) 以上が必要 *1
  • x86, x64, ARM64 に対応

インストール方法

以下のリンクからダウンロードできる (Windows Store 経由) www.microsoft.com

ショートカット キー

既定のショートカット キーについて以下に示す。

ショートカット キーが他のアプリケーションと競合してしまっているケースや、自分の好みと異なる場合は、JSON ファイル形式の設定ファイルを変更することで、好みの設定変更できる。

なお、コマンドライン上で動作するアプリケーションのショートカット キーとバッティングしないために、Shift キー付きでバインドされていることが多い。

基本操作

操作 キー
コピー Ctrl + Shift + C
貼り付け Ctrl + Shift + V
検索 Ctrl + Shift + F

タブの操作 *2

操作 キー
プロファイル 0 のタブを開く Ctrl + Shift + 1
プロファイル 1 のタブを開く Ctrl + Shift + 2
プロファイル 2 のタブを開く Ctrl + Shift + 3
プロファイル 3 のタブを開く Ctrl + Shift + 4
プロファイル 4 のタブを開く Ctrl + Shift + 5
プロファイル 5 のタブを開く Ctrl + Shift + 6
プロファイル 6 のタブを開く Ctrl + Shift + 7
プロファイル 7 のタブを開く Ctrl + Shift + 8
プロファイル 8 のタブを開く Ctrl + Shift + 9
現在のタブを複製 Ctrl + Shift + D
次のタブ Ctrl + Tab
前のタブ Ctrl + Shift + Tab
タブ 0 に移動 Ctrl + Alt + 1
タブ 1 に移動 Ctrl + Alt + 2
タブ 2 に移動 Ctrl + Alt + 3
タブ 3 に移動 Ctrl + Alt + 4
タブ 4 に移動 Ctrl + Alt + 5
タブ 5 に移動 Ctrl + Alt + 6
タブ 6 に移動 Ctrl + Alt + 7
タブ 7 に移動 Ctrl + Alt + 8
タブ 8 に移動 Ctrl + Alt + 9

ペインの操作

既定ではペイン操作用のショートカットは設定されていない様なので、私は以下のように設定に追加している。*3

{
    "command" : "splitHorizontal",
    "keys" : ["ctrl+shift+-"]
},
{
    "command" : "splitVertical",
    "keys" : ["ctrl+shift+|"]
},
{
    "command" : "moveFocusUp",
    "keys" : ["ctrl+alt+up"]
},
{
    "command" : "moveFocusDown",
    "keys" : ["ctrl+alt+down"]
},
{
    "command" : "moveFocusLeft",
    "keys" : ["ctrl+alt+left"]
},
{
    "command" : "moveFocusRight",
    "keys" : ["ctrl+alt+right"]
},
{
    "command" : "resizePaneUp",
    "keys" : ["shift+alt+up"]
},
{
    "command" : "resizePaneDown",
    "keys" : ["shift+alt+down"]
},
{
    "command" : "resizePaneLeft",
    "keys" : ["shift+alt+left"]
},
{
    "command" : "resizePaneRight",
    "keys" : ["shift+alt+right"]
},
{
    "command" : "closePane",
    "keys" : ["ctrl+shift+w"]
}

その他

操作 キー
設定を開く Ctrl + ,

なお、設定を開くと、JSON ファイルに関連付けされている既定のアプリケーションが起動するので、注意すること。 特に Visual Studio を使用している開発者は、既定が Visual Studio になっていることがあるので、"Visual Studio Code" など比較的軽量なエディタに関連付けしておくと良い。

コマンドライン引数

Windows Terminal をコマンドラインから起動する場合は wt で起動できる。

wt

また、バージョン 0.9 で、コマンドライン引数にも対応している。 バージョン 0.9 でのヘルプを以下に示す。

wt - the Windows Terminal
Usage: [OPTIONS] [SUBCOMMAND]

Options:
  -h,--help                   Print this help message and exit

Subcommands:
  new-tab                     Create a new tab
  split-pane                  Create a new split pane
  focus-tab                   Move focus to another tab

ディレクトリを開く

特定のディレクトリを開くには、"-d" オプションを使用する。"C:\Windows" を開く例を以下に示す。

wt -d C:\Windows

現在のディレクトリを Windows Terminal で起動したい場合は次のようにする。*4

wt -d .

応用としては、Explorer.exe のアドレス バーで cmd や powershell を起動している方は、代わりに "wt -d ." を入力することで、Windows Terminal を起動することができる (デフォルトのプロファイルで起動する)。

レイアウトを変更する (タブを開く/ペインを分割する)

パラメータを指定することで、起動時にタブやペインの構成を指定することができる。 なお、";" の前後には必ず空白を挿入する必要があるので、注意すること。

追加でタブを開く場合は、new-tab コマンドを使用する。 下記の例では、既定のプロファイルのタブに加えて、PowerShell のタブが表示される。

wt ; new-tab -p "Windows PowerShell"

なお、-p オプションには、プロファイル名を指定する必要がある。プロファイル名が不明な場合は、設定ファイルのプロファイルを確認すると良い。たとえば、下記のプロファイルの場合、"name" が示す "Windows PowerShell" がプロファイル名である。

{
    "tabTitle": "PowerShell",
    "suppressApplicationTitle": true,
    "acrylicOpacity": 0.5,
    "background": "#012456",
    "closeOnExit": true,
    "colorScheme": "Campbell",
    "commandline": "powershell.exe",
    "cursorColor": "#FFFFFF",
    "cursorShape": "bar",
    "fontFace": "Consolas",
    "fontSize": 11,
    "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
    "historySize": 9001,
    "icon": "ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png",
    "name": "Windows PowerShell",
    "padding": "0, 0, 0, 0",
    "snapOnInput": true,
    "startingDirectory": "%USERPROFILE%",
    "useAcrylic": false
},

プロファイル名を指定する方法の他に、実行ファイルを指定する方法もある。たとえば、WSL の既定を開く場合は、以下のように指定する。

wt ; new-tab wsl.exe

ペインを分割する場合は、split-pane コマンドを使用する。 下記の例では、ペインが分割され、左側は既定のプロファイル、右側は PowerShell が表示される。

wt ; split-pane -p "Windows PowerShell"

なお、分割する方向は 水平を示す -H 、または垂直を示す -V オプションを指定すればよい。指定しない場合は、垂直 (縦) に分割される。

水平 (横) に分割する場合の例を以下に示す。

wt ; split-pane -p "Windows PowerShell" -H

参考情報

公式ブログ

Windows Terminal の情報は以下のブログで紹介されている。新機能の紹介なども説明しているので、参考にされたい。

devblogs.microsoft.com

ユーザー ドキュメント

公式なユーザー向けのドキュメントは GitHub 上で公開されているので、機能について知りたい場合は以下を参照すると良い。

github.com

*1:この要件は、バージョンアップで変更になる可能性がある

*2:それにしても、なぜタブ番号は 0 オリジンなのか...

*3:なお、1Password を使っている場合、"Ctrl + Sfhit + \" が競合してしまうので、1 Password 側を変更すると良い

*4:"." は現在のパスを示す

Azure DevOps Server または Team Foundation Server でエラーが出たのを調査するときの話

私が参考にしていた公式の解説ブログ記事が MSDN ブログ廃止の影響を受けて消えてしまったので、主に自分向けのメモとして記事にすることにした。

管理ツール (_oi) を確認する

Team Foundation Server 2012 以降では管理ツールが統合され、_oi でアクセスできる。これは、Azure DevOps Server でも変わらない。

devblogs.microsoft.com

管理ツールにアクセスするには、自身の Team Fondation Server または Azure DevOps Server の URL に加えて /_oi を足した URL にアクセスすればよい。*1 以下に URL 例を示す。

Team Foundation Server 2012 - 2018 の場合 :

http://yourservername:8080/tfs/_oi

Azure DevOps Server 2019 以降を新規に構築した場合 :

https://yourservername/_oi

アクテビティログを確認する

Team Foundation Server および Azure DevOps Server には内部的にアクテビティ ログを保存する仕組みがある。 問題が発生しているチーム プロジェクト コレクション (TPC) のデータ ベースに対して、以下のクエリを実行すると取得できる。

    SELECT *
    FROM tbl_command c
    LEFT JOIN tbl_parameter p ON c.commandid = p.commandid

なお、サーバー全体に対するアクテビティは "Configuration" データベースに含まれることがある (プロジェクト コレクションの作成など) 。

(補足) データベース名の既定は Team Foundation Server 2018 までは Tfs_* だったが、Azure DevOps Server 2019 以降の既定は AzureDevOps_* に変更されている。Team Foundation Server 2018 以前のバージョンから移行した場合は、引き続き Tfs_* が使われるが、新規に構築した場合は違いがあるので、プロジェクトコレクション名 to データベース名などの変換をスクリプトで自動化している場合は注意するとよい。

イベント ログを確認する

Team Foundation Server および Azure DevOps Server には内部でエラーが発生するとイベントログに記録する仕組みがある。 ほとんどのケースでは、アプリケーション ログに TFS Services の名前で記録される。なお、Azure DevOps Server でも名前は同じである。

*1:なぜか、管理画面などからリンクされていないので、管理者の間では "秘密のツール" ということになっているとか、いないとか。

docker で遊んだ後のお片付け

docker でいろいろと試していると、--rm をつけ忘れてコンテナが残ってしまうといったことが良くあります。 いろいろと試した後で作ったコンテナやイメージをごそっと消したいというときに使うコマンドを備忘録として書いておきます。

すべてのコンテナを停止させる

(補足) 実行中のコンテナは削除できないので、停止させます。

docker stop $(docker ps -q)

すべてのコンテナを消す

docker rm $(docker ps -q -a)

すべてのイメージを消す

docker rmi $(docker images -q)

Windows な人に向けて

コマンド プロンプト (cmd.exe) では、$() を評価できないため正しく動きません。PowerShell (or pwsh) を使いましょう。

Azure Pipelines の Pool を調べる

Azure DevOps 向けの拡張機能を Azure CLI にインストールし、次のコマンドを実行すればよい。 なお、事前に az login でログインしておく必要がある。

アクセスできるプールは組織 (orgnization, 旧VSTSアカウント) 次第なので、下記の結果は参考程度に見てほしい。

C:\Works>az pipelines pool list -o table
This command group is in preview. It may be changed/removed in a future release.
ID    Name                             Is Hosted    Pool Type
----  -------------------------------  -----------  -----------
1     Default                          False        automation
2     Hosted                           True         automation
4     Hosted VS2017                    True         automation
5     Hosted macOS                     True         automation
6     Hosted Ubuntu 1604               True         automation
7     Hosted Windows Container         True         automation
8     Hosted Windows 2019 with VS2019  True         automation
9     Hosted macOS High Sierra         True         automation
10    Azure Pipelines                  True         automation

なお、YAML で pool の指定をする場合の名前はこのコマンドでは取得できず、以下の URL の記載を確認する必要がある *1

docs.microsoft.com

*1:なんで取れないねん!と私は思う...

購入した製品が動かないので Microsoft と Apple から電話サポートをしてもらった話

久しぶりにブログを書こうと思ったのは、比較的短い間隔で購入した製品が動かないという不幸が続き、カスタマー サポートを Microsoft と Apple から受けたのがだ、その時の印象がだいぶ違うことに驚いたからだ。

これを読む人が同じようにサポートを受ける人なのか、サポートをする側の人なのかはわからないが、どちらにでも有益な情報になるだろうと思って書いた。ただ個人的には、サポートをする側の人たちがこの内容を見てもう少しサポートを改善しようと思ってくれると嬉しい。

Microsoft / Apple のカスタマー サポートの流れの比較

それぞれ、個人向けサポート (コンシューマー サポート) 窓口での対応の話である。企業サポートや、それ以外の対応ではサポート体制が大きく異なることがあるので、注意してほしい。

対応ステップ        Microsoft Apple
カスタマー サポートに電話する方法 Microsoft のサポートページに記載されている電話番号に連絡する。ちなみに、Windows 10 に搭載されている [問い合わせ] アプリには、個人顧客向けのリンクがないので注意。電話番号も問い合わせ内容によって複数存在するため、どれに電話をするのかは注意が必要。 サポートページより対象の製品を選び、[すぐアドバイザーと連絡したい]をクリックすればよい。このとき、何分ぐらいで連絡がとれるのかも記載がある。
電話サポートまでの流れ 音声ガイダンスに従って、問い合わせ内容を電話番号のプッシュで選択。担当窓口が対応可能になるまで少し待つ ウェブサイト上で必要な情報 (シリアル番号、問い合わせ内容) を記載したうえで、自分自身の電話番号を記入する。折り返し、担当者より電話がくる。
問題のヒアリング サポート担当者にどのような事象だったのかを話す。その際に、電話が切られてしまった場合に備えて、名前と電話番号を確認される。 アドバイザーよりWebサイトに記載した事情について詳細に話を聞かれる。たとえば、「動かない」と記載した場合には、どのように動かないのか、などについて確認される。ヒアリングが完了すると、折り返しエンジニアから連絡しますといわれてこの電話は終了。
問題の対処 ヒアリングした事象についてサポート担当者が対処方法を確認するので、しばし待つ。対処方法について説明を受け、サポート担当者と一緒に問題解決を試みる エンジニアから連絡が来る。問題の事象について再度確認を受けて齟齬がないことを確認される。エンジニアと一緒に問題解決を試みる

なお、問題の事象次第で、その後の対応については変わるため、問題の対処後については比較しない。

特記すべき体験について以下に書いておく。

Apple のアドバイザーはとにかくエモい

釣りタイトルみたいで申し訳ないが (笑)、アドバイザーの対応はとにかく低姿勢で丁寧に対応してくれる。 私の場合、Apple TV が自身の TV で接続しても画面が表示されないという問題だったわけだが、「〇〇様が Apple TV でビデオを見られると楽しみにされていたのに画面が表示されないということなのですね...それは大変申し訳ございません!」というような言葉を感情をこめて言われる。私も、「おぉぉ...この人、私の問題を理解している!」という気持ちになった。期待して購入した製品について、動かなくて「残念だ」という気持ちに対して、「残念でしたよね」と共感することは大事なのだなぁ、と改めて思った。Microsoft のサポートでも他のサポートでもそうだが、基本的には「なるほど、そうですか。」という対応で終わってしまうが、購入した製品が期待する機能をはたしていないとき、こういった対応は必要だ。

Apple のサポート体制について私は知らないが、私の予想ではアドバイザーは問題のハンドリングをしているのだと思う。つまり、アドバイザー自身が直接問題を解決するのではなく、ユーザーからヒアリングしたうえで適切な場所に誘導しているのだろう、と予想している。実際、このアドバイザーからは「どんな問題か」についてはいろいろと聞かれるが、簡単な「対処」については何も聞かれなかった。

電話受付時、事象によっては顧客は焦っていたり、怒っていたりしているだろう。そういう時にこういったアドバイザーが丁寧にそして冷静に問題を仕分け、適切な場所に振り分けるのには特殊なスキルが必要だ。それは、技術サポートとはちょっと違う。

電話口で "待つ" ということ

現在、企業は様々なサポート手段を持っている。たとえば、電話、メール、チャット、ボットなど。企業によっては Twitter などの SNS を巡回し、自社製品に不満を言っている Tweet を見つけて、対処方法を連絡するということをしていたりもする。 ただ、緊急であったり、状況や問題について整理ができていない場合、あなたは "電話サポート" を選ぶはずだ。

電話サポートにおける問題の 1 つは、その対応時間だ。これを見ている人が長電話は慣れたものかどうかはわからないが、私は日頃、スマホで 1 時間も電話などしない。今回の Microsoft の場合は、購入した商品が初期不良だったという問題だったが、こんなシンプルな問題でも結局 1 時間以上電話することになった。おそらく、サポート担当者 (オペレーター) 側はヘッドフォンなどで対応しているのだろうが、スマホというそこそこのダンベルを1時間以上耳に当てて電話をするのは大変疲れた (手がしびれて途中で諦めそうになった)。

電話を長時間にさせる原因は明らかで、すべてを電話口で対応するためだ。たとえば、上記の比較表では Microsoft は1回の電話ですべての作業が行われているが、Apple の場合はアドバイザーとそのあとのエンジニアとの 2 回に分割されている。しかも、Apple の場合は電話前に問題を伝えているので話がスムーズ (Apple 側が電話前にどのような対処をするかが明らか) なのに対して、Microsoft 側は電話口での質問に急に対応する必要がるため、時間がかかるのだ。

そう、Apple のサポートはお客様のためにも、自分たちのためにもシステムが良くできている。

担当者が変わらないという安心感

Apple のサポートで特に関心したのは、アサインされたエンジニアが開口一番に「では、私がこの問題を最後まで一緒に解決に当たらせていただきます」と言ったことだった。

サポートを受けたことがない人は「普通じゃないの?」と思ったかもしれないが、これは普通ではない。多くのサポート対応というのは電話するたびに担当者 (オペレーター) が変わるのが普通だ。このため、問い合わせごとに番号をつけて管理をし、"引継ぎ" をする。

実際、Apple の場合は 1 回の電話では解決せずにこのあと、何度も電話をすることになったが、担当者から自身に直接電話可能な電話番号を連絡してもらえたので、新たな事象などをスムーズに話すことができた (何度も対応番号を伝えたり、引継ぎできていなかった詳細について話を伝えるのは大変心が折れる)。

(おまけ)

当然、Apple の担当者も私だけを担当しているわけではないので、暇ではない。途中何度も電話をかけてもつながらないという事象に遭遇した。すると、1 - 2時間ぐらいでその担当者の上司から連絡があり、「担当者が多忙で電話に出られなくて申し訳ありません。状況は理解していますので、私もサポートさせていただきます。」と言われた。「結局、1人の担当者じゃないんかい!」というツッコミがくるかもしれないが、この人は再度事情を説明することもなく、完全に状況を理解して適切な対応をしてくれた。

結局、私の購入した Apple TV はテレビとの相性問題であるという結論を受けて、返金対応をすることになった。担当者から「せっかく買っていただいたのに大変申し訳ない、Apple に問い合わせて担当者が返品してもらってくださいと言っていたと言ってください、もしかしたらテレビ メーカーのサポートにも問合せていただくと対処方法が見つかるかもしれません」といった言葉をかけてもらった。最後まで非常に丁寧かつ親切な対応だったと思う。

面白かったのは、その話を受けて相性問題だからテレビ メーカーのサポートにも連絡しようと電話をしたら「あー、そういう "変な" デバイスはつながないでください」と言われたことだ。さすが某有名企業、としか言いようがない。

IT (ICT) サポートについて思うこと

私もこの業界にいてそこそこ長くなってきたので、IT の問題の複雑性については理解しているつもりだ。

たとえば、周辺機器の不具合の場合、「認識しない」という問題が、コンピューター側の問題 (つまり、ドライバーやソフトウェア) の問題として発生しているのか、周辺機器自体 (つまり、ハードウェア) の問題として発生しているのかが判断しにくい。 そして、そういった問題を切り分ける方法として「"別の〇〇" はありますか?」という言葉を聞くことがあるかもしれない。たとえば、テレビに映らないのであれば「別の HDMI ケーブルで接続してみていただけますか?」といったセリフだ。ただ、こういったセリフに対して、私はときどき「ん???」となることがある。

たとえば、「では、別のテレビに接続してみてください」、「では、別の Surface に接続してください」といったセリフ。普通、個人はそんなに高額なデバイスを当然のように複数持っていない。仮にそれがある可能性を期待していたのだとしても「もしあれば、なんですが...」といえば全然印象が違うのになぁ、と思った。

ちなみに、あるサポートでは「対応してないモニターなので、モニターを買いなおしてください」と言われて、お茶を吹きそうになったことすらある。心の中で、「お前なんか、購入した HEMS が動かなくて、家を買いなおせ!」と思ったのは内緒である。

おわりに

この文章を読むと、「Apple すごい!Microsoft けしからん!」と思うかもしれないが、それは事実とは違う可能性がある。なぜなら、その印象は私からの感情バイアスを受けているからだ。

実際、マイクロソフトのサポートでも素晴らしい対応をしてくれるときもあれば、そうでないときもある。それは他の企業のサポートであっても同じことだ。私が書きたかったのは「サポートの仕組み/システムを変えれば顧客体験がこんなにも違うのか」と感じたこと、そして対応の仕方や言葉一つでカスタマーサポートの印象は大きく違うのだと感じた体験を共有したかったからだ。特定の企業を批判する目的で書いたわけではないことを十分に理解してほしい。

Azure DevOps Security Namespaces/Actions

Default

AccountAdminSecurity

Name Display name
Create Create account resource
Modify Modify account resource
Read Read account resource

Analytics

Name Display name
Administer Manage analytics permissions
ExecuteUnrestrictedQuery Execute query without any restrictions on the query form
Read View analytics
ReadEuii Read EUII data
Stage Push the data to staging area

AnalyticsViews

Name Display name
Delete Delete shared Analytics views
Edit Edit shared Analytics views
Execute Execute Analytics views
ManagePermissions Manage
Read View shared Analytics views

Boards

Name Display name
ChangeMetadata Change board metadata (name, columns, rows, settings, etc)
Create Create a board
Delete Delete the board
Manage (blank)
MoveCard Add, move or remove cards in the board
View View the board

BoardsExternalIntegration

Name Display name
Read View boards external integrations
Write Write boards external integrations

Collection

Name Display name
CREATE_PROJECTS Create new projects
DELETE_FIELD Delete field from account
DIAGNOSTIC_TRACE Alter trace settings
GENERIC_READ View collection-level information
GENERIC_WRITE Edit collection-level information
MANAGE_TEMPLATE Manage process template
MANAGE_TEST_CONTROLLERS Manage test controllers
SYNCHRONIZE_READ View system synchronization information
TRIGGER_EVENT Trigger events

CrossProjectWidgetView

Name Display name
GenericRead View instance-level information

DashboardsPrivileges

Name Display name
Create Create dashboard
Delete Delete dashboard
Edit Edit dashboard
ManagePermissions ManagePermissions
MaterializeDashboards Materialize Dashboards
Read Read

DataProvider

Name Display name
Read View data provider entities

EventSubscriber

Name Display name
GENERIC_READ View
GENERIC_WRITE Edit

EventSubscription

Name Display name
CREATE_SOAP_SUBSCRIPTION Create a SOAP subscription
GENERIC_READ View
GENERIC_WRITE Edit
UNSUBSCRIBE Unsubscribe

Favorites

Name Display name
GenericRead View instance-level information
GenericWrite Edit instance-level information

Graph

Name Display name
ReadByPersonalIdentifier Read by personal identifier
ReadByPublicIdentifier Read by public identifier

Identity

Name Display name
CreateScope Create identity scopes
Delete Delete identity information
ManageMembership Manage group membership
Read View identity information
RestoreScope Restore identity scopes
Write Edit identity information

IdentityPicker

Name Display name
ReadBasic Read basic Identity Picker properties
ReadRestricted Read restricted Identity Picker properties

Job

Name Display name
Queue Queue background jobs
Read View background job information
Update Manage background jobs

Location

Name Display name
Read Read service definitions and/or access mappings.
Write Write service definitions and/or access mappings.

Process

Name Display name
AdministerProcessPermissions Administer process permissions
Create Create process
Delete Delete process
Edit Edit process
ReadProcessPermissions View Process

Project

Name Display name
ADMINISTER_BUILD Administer a build
AGILETOOLS_BACKLOG Agile backlog management.
BYPASS_PROPERTY_CACHE Bypass project property cache
BYPASS_RULES Bypass rules on work item updates
CHANGE_PROCESS Change process of team project.
Delete Delete team project
DELETE_TEST_RESULTS Delete test runs
EDIT_BUILD_STATUS Edit build quality
GENERIC_READ View project-level information
GENERIC_WRITE Edit project-level information
MANAGE_PROPERTIES Manage project properties
MANAGE_SYSTEM_PROPERTIES Manage system project properties
MANAGE_TEST_CONFIGURATIONS Manage test configurations
MANAGE_TEST_ENVIRONMENTS Manage test environments
PUBLISH_TEST_RESULTS Create test runs
RENAME Rename team project
START_BUILD Start a build
SUPPRESS_NOTIFICATIONS Suppress notifications for work item updates
UPDATE_BUILD Write to build operational store
UPDATE_VISIBILITY Update project visibility
VIEW_TEST_RESULTS View test runs
WORK_ITEM_DELETE Delete and restore work items
WORK_ITEM_MOVE Move work items out of this project
WORK_ITEM_PERMANENTLY_DELETE Permanently delete work items

ProjectAnalysisLanguageMetrics

Name Display name
Read View Project Analysis language metrics data
Write Write Project Analysis language metrics data

Proxy

Name Display name
Manage Manage proxies
Read Read proxies

Registry

Name Display name
Read Read registry entries
Write Write registry entries

Security

Name Display name
Read Read

Server

Name Display name
GenericRead View instance-level information
GenericWrite Edit instance-level information
Impersonate Make requests on behalf of others
TriggerEvent Trigger events

ServiceEndpoints

Name Display name
Administer Administer Endpoint
Create Create Endpoint
View View Endpoint
ViewAuthorization View Authorization

ServiceHooks

Name Display name
DeleteSubscriptions Delete Subscriptions
EditSubscriptions Edit Subscription
PublishEvents Publish Events
ViewSubscriptions View Subscriptions

ServicingOrchestration

Name Display name
Cancel Cancel Servicing Orchestration jobs
Queue Queue Servicing Orchestration jobs
Read View Servicing Orchestration information

SettingEntries

Name Display name
Read Retrieve setting entries
Write Write setting entries

Social

Name Display name
GenericRead View instance-level information
GenericWrite Edit instance-level information

StrongBox

Name Display name
AddItem Add Items to the StrongBox Drawer.
Administer Administer StrongBox Permissions.
AdministerDrawer Administer permissions for the StrongBox Drawer.
CreateDrawer Create a StrongBox Drawer.
DeleteDrawer Delete as StrongBox Drawer.
DeleteItem Delete Items from the StrongBox Drawer.
GetItem Retrieve Items from the StrongBox Drawer.

Tagging

Name Display name
Create Create tag definition
Delete Delete tag definition
Enumerate Enumerate tag definitions
Update Update tag definition

TestManagement

Name Display name
Read Read TestManagement

UtilizationPermissions

Name Display name
QueryUsageSummary Query Others' Usage

ViewActivityPaneSecurity

Name Display name
Read View only entities

WebPlatform

Name Display name
Read View web platform entities

WorkItemsHub

Name Display name
View View work items hub

WorkItemTracking

Name Display name
CrossProjectRead Cross Project Read Of WorkItemTracking Resources
Read Read WorkItemTracking
ReadHistoricalWorkItemResources (blank)
ReadRules Read rules only if permissions are avaliable
TrackWorkItemActivity Track work item read and write for a user

WorkItemTrackingConfiguration

Name Display name
Read View work item tracking configuration

VersionControl

VersionControlItems

Name Display name
AdminProjectRights Manage permissions
Checkin Check in
CheckinOther Check in other users' changes
Label Label
LabelOther Administer labels
Lock Lock
ManageBranch Manage branch
Merge Merge
PendChange Pend a change in a server workspace
Read Read
ReviseOther Revise other users' changes
UndoOther Undo other users' changes
UnlockOther Unlock other users' changes

VersionControlItems2

Name Display name
AdminProjectRights Manage permissions
Checkin Check in
CheckinOther Check in other users' changes
Label Label
LabelOther Administer labels
Lock Lock
ManageBranch Manage branch
Merge Merge
PendChange Pend a change in a server workspace
Read Read
ReviseOther Revise other users' changes
UndoOther Undo other users' changes
UnlockOther Unlock other users' changes

VersionControlPrivileges

Name Display name
AdminConfiguration Administer source control configurations
AdminConnections Administer source control connections
AdminShelvesets Administer shelved changes
AdminWorkspaces Administer workspaces
CreateWorkspace Create a workspace

Workspaces

Name Display name
Administer Administer the workspace
Checkin Check in changes to the workspace
Read View workspace information
Use Use the workspace

Git

Git Repositories

Name Display name
Administer Administer
CreateBranch Create branch
CreateRepository Create repository
CreateTag Create tag
DeleteRepository Delete repository
EditPolicies Edit policies
ForcePush Force push (rewrite history, delete branches and tags)
GenericContribute Contribute
GenericRead Read
ManageNote Manage notes
ManagePermissions Manage permissions
PolicyExempt Bypass policies when pushing
PullRequestBypassPolicy Bypass policies when completing pull requests
PullRequestContribute Contribute to pull requests
RemoveOthersLocks Remove others' locks
RenameRepository Rename repository

WorkItem

Plan

Name Display name
Delete Delete
Edit Edit
Manage Manage
View View

WorkItemQueryFolders

Name Display name
Contribute Contribute
Delete Delete
FullControl Full Control
ManagePermissions Manage permissions
Read Read
RecordQueryExecutionInfo Record query execution information

WorkItemTrackingAdministration

Name Display name
DestroyAttachments Destroy attachments
ManagePermissions Manage permissions

WorkItemTrackingProvision

Name Display name
Administer Administer
ManageLinkTypes Manage work item link types

Build

Build

Name Display name
AdministerBuildPermissions Administer build permissions
DeleteBuildDefinition Delete build definition
DeleteBuilds Delete builds
DestroyBuilds Destroy builds
EditBuildDefinition Edit build definition
EditBuildQuality Edit build quality
ManageBuildQualities Manage build qualities
ManageBuildQueue Manage build queue
OverrideBuildCheckInValidation Override check-in validation by build
QueueBuilds Queue builds
RetainIndefinitely Retain indefinitely
StopBuilds Stop builds
UpdateBuildInformation Update build information
ViewBuildDefinition View build definition
ViewBuilds View builds

BuildAdministration

Name Display name
AdministerBuildResourcePermissions Administer build resource permissions
ManageBuildResources Manage build resources
UseBuildResources Use build resources
ViewBuildResources View build resources

ReleaseManagement

ReleaseManagement

Name Display name
AdministerReleasePermissions Administer release permissions
CreateReleases Create releases
DeleteReleaseDefinition Delete release pipeline
DeleteReleaseEnvironment Delete release stage
DeleteReleases Delete releases
DeploymentSummaryAcrossProjects Deployment summary across projects
EditReleaseDefinition Edit release pipeline
EditReleaseEnvironment Edit release stage
ExportReleaseDefinition Export release definition
ManageDeployments Manage deployments
ManageReleaseApprovers Manage release approvers
ManageReleases Manage releases
ManageReleaseSettings Manage release settings
ManageTaskHubExtension Manage TaskHub Extension
ViewCDWorkflowEditor View CD work flow editor
ViewExternalArtifactCommitsAndWorkItems View external artifact commits and work items
ViewLegacyUI View legacy UI
ViewReleaseDefinition View release pipeline
ViewReleases View releases
ViewTaskEditor View task editor

LabExecution

TeamLabSecurity

Name Display name
Create Create
Delete Delete
DeleteLocation DeleteLocation
Edit Edit
ManageChildPermissions ManageChildPermissions
ManageLocation ManageLocation
ManagePermissions ManagePermissions
ManageSnapshots ManageSnapshots
ManageTestMachines ManageTestMachines
Pause Pause
Read Read
Start Start
Stop Stop
Write Write

Discussion

Discussion Threads

Name Display name
Administer Manage discussion permissions
GenericContribute Contribute to discussions
GenericRead View discussions
Moderate Moderate discussions

DistributedTask

DistributedTask

Name Display name
AdministerPermissions Administer Permissions
Create Create
Listen Listen
Manage Manage
Use Use
View View

Environment

Name Display name
Administer Administer Permissions
Create Create
Manage Manage
Use Use
View View

Library

Name Display name
Administer Administer library item
Create Create library item
Use Use library item
View View library item
ViewSecrets View library item secrets

MetaTask

Name Display name
Administer Administer task group permissions
Delete Delete task group
Edit Edit task group

Integration

CSS

Name Display name
CREATE_CHILDREN Create child nodes
Delete Delete this node
GENERIC_READ View permissions for this node
GENERIC_WRITE Edit this node
MANAGE_TEST_PLANS Manage test plans
MANAGE_TEST_SUITES Manage test suites
WORK_ITEM_READ View work items in this node
WORK_ITEM_WRITE Edit work items in this node

Iteration

Name Display name
CREATE_CHILDREN Create child nodes
Delete Delete this node
GENERIC_READ View permissions for this node
GENERIC_WRITE Edit this node

Chat

Chat

Name Display name
AddRemoveChatRoomMember Add/Remove Chat Room Member
CloseChatRoom Close Chat Room
CreateChatRoom Create Chat Room
DeleteChatRoom Delete Chat Room
DeleteChatRoomMessage Delete Chat Room Message
ManageChatPermissions Manage Chat Permissions
ReadChatRoomMessage Read Chat Room Message
ReadChatRoomMetadata Read Chat Room Metadata
ReadChatRoomTranscript Read Chat Room Transcript
UpdateChatRoomMessage Update Chat Room Message
UpdateChatRoomMetadata Update Chat Room Metadata
WriteChatRoomMessage Write Chat Room Message