ffmpeg で動画を変換して 1TB 節約した話
はじめに
私は 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 の開発者界隈では広く使われています。インストール方法などについてはこの記事で詳しく扱いませんが、下記を見れば簡単に導入できます。
Homebrew で ffmpeg をインストールするには、以下のコマンドを実行します。
brew install ffmpeg
画質の考察
動画を変換する上で最も重要なことは「どういった画質として保存するか」であることは言うまでもありません。基本的に、画質が良ければ自然と大きくなり、小さくしようとすると画質は悪くなるものです。
元の動画の画質を知るには ffprobe
コマンドを使用します。
ffprobe も ffmpeg も単純に実行すると、バナーと呼ばれる情報が表示されてしまうので、以下のように -hide_banner
オプションを付与することをオススメします。
ffprobe -hide_banner /path/to/sample.avi
残念ながら「良い感じ最適化」といった曖昧な指定はできないため、動画素人な私はどの画質にするのかを一番悩みました。
結局、世界最大の動画サイトである YouTube の解像度やビットレートを参考にすることにしました。
簡単に表にまとめると、以下のようになります (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 の違いはインターネット上に大量に記事がありますので、そちらを参考にしてください。
個人的には、富士通研究所の以下の記事がわかりやすかったです。
しかし、実験してみると変換が非常に遅く、時間がかかるため今回は "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!
開発者のための Windows Terminal 入門
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 の情報は以下のブログで紹介されている。新機能の紹介なども説明しているので、参考にされたい。
ユーザー ドキュメント
公式なユーザー向けのドキュメントは GitHub 上で公開されているので、機能について知りたい場合は以下を参照すると良い。
Azure DevOps Server または Team Foundation Server でエラーが出たのを調査するときの話
私が参考にしていた公式の解説ブログ記事が MSDN ブログ廃止の影響を受けて消えてしまったので、主に自分向けのメモとして記事にすることにした。
管理ツール (_oi) を確認する
Team Foundation Server 2012 以降では管理ツールが統合され、_oi でアクセスできる。これは、Azure DevOps Server でも変わらない。
管理ツールにアクセスするには、自身の 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。
*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
- Analytics
- AnalyticsViews
- Boards
- BoardsExternalIntegration
- Collection
- CrossProjectWidgetView
- DashboardsPrivileges
- DataProvider
- EventSubscriber
- EventSubscription
- Favorites
- Graph
- Identity
- IdentityPicker
- Job
- Location
- Process
- Project
- ProjectAnalysisLanguageMetrics
- Proxy
- Registry
- Security
- Server
- ServiceEndpoints
- ServiceHooks
- ServicingOrchestration
- SettingEntries
- Social
- StrongBox
- Tagging
- TestManagement
- UtilizationPermissions
- ViewActivityPaneSecurity
- WebPlatform
- WorkItemsHub
- WorkItemTracking
- WorkItemTrackingConfiguration
- VersionControl
- Git
- WorkItem
- Build
- ReleaseManagement
- LabExecution
- Discussion
- DistributedTask
- Integration
- Chat
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 |