サマータイムの恐怖

日本が、サマータイムを導入しようとしているらしい。

これだけでも狂気の沙汰であるが、それも2時間もの幅で、それも2年間限定だという。

前代未聞の愚行だが、なんとそれを支持する人が過半数であるという。

都市機能が麻痺し、日本が国際的信用が損なわれ、人命が失われるまさにサイバーテロなのだが、 「日本は自国にサイバーテロを発生させよ!!」と考えているのだろうか。

これがどけほど愚かしいかは詳細に解説するには私には知識が足りないし、 外でも色々と解説されているので、部分的に切り取ってお話しようと思う。

これはほんとひとかけらにすぎないことを理解してほしい。

もっと理解したければ、Togetterに秀逸なまとめがある

サマータイムの難しさ

まつもとゆきひろ氏いわく

Rubyの生みの親であるまつもとゆきひろ氏が著書「コードの世界」の中で次のような話をしている

個人的な経験になりますが,2007年11月に米国,ノースカロライナ州で開催されたRUbyConfの最終日と、2008年3月にチェコのプラハで開催されたEuRuKoの2日目が,ちょうどサマータイム切り替えの日でした。 どちらも前日から主催者が繰り返し「明日からタイムゾーンが切り替わるから時間を間違えないように」と叫んでいましたが,それでも時計を合わせるのを忘れる人がそれなりにいたようです。 ホテルの部屋の時計も自動で調整されるわけではないので,かなり注意しないとうっかりミスをする人が続出しそうです。 電波時計なら自動調整されますが,国中すべての時計をいっせいに電波時計にするわけにもいきませんしね。

ずいぶん昔のことになりますが,Ruby 1.4.4のころ (2000年ころ) には,このサマータイムの扱いにバグがありました。 そのころようやく増え始めたヨーロッパのユーザーからのレポートで発覚したのですが,サマータイムの始まりと終わりの数時間だけ時刻がズレるという典型的な境界条件バグでした。 日本にはサマータイムがないので,私自身が問題を理解できず,このバグを直すのに大変苦労しました。 この経験から,安易なサマータイム導入は,ソフトウエアのトラブルを多発させることになり,最悪の場合,社会問題に,そうでなくても,人知れず数多くのプログラマが涙を飲むことになりそうなので,とても賛成できません。

巻き戻る時間、消失する時間

サマータイムが難しいのはなんといっても「時間が直線状に進まなくなる」ことである。

たとえば、2020年8月1日 00:00:00(JST)に夏時間(DST)が適用になるとする。 すると、2020年7月31日 23:59:59の次の1秒は2020年8月1日 2:00:00になる。 つまり2020年9月1日の0時から2時が消滅する。

一方、2020年9月1日にDSTが終了すると、が2020年8月31日 23:59:59の次は2020年8月31日 22:00:00である。 つまり、2020年8月31日の22時から24時までの間は2回繰り返される。

この場合、たとえば

  • 2020年8月31日23時放送予定の番組はJSTなのかJDTなのかによって2時間ズレる
  • 約束の時間を設定してしまうとすれ違いが発生しうる
  • 日中にやるとさらに問題は深刻に。就業時間も2時間伸びる
  • 0時更新とすると「日をまたぐ瞬間」がなくなってしまう。日次、月次でスケジュールされているジョブが発生しない可能性があり、重大事故が発生する可能性もある

ちなみに、サマータイムの想像はまつもとゆきひろ氏が苦戦していることからも明らかだと思うが、想像よりずっと難しい。 実際私もこれを書いている最中に、「サマータイムに入るときに時間が戻り、サマータイム終了時に時間が進む」と思って途中まで書いていた。 正解は逆である。

UTCから単純計算できない問題

日本は国内時差がないため、基本的にJSTは単純にUTCからオフセットした時間として扱えていた。 ここにJDTが加わるとJSTは計算可能な基準の値ではなくなってしまう。

たとえば日本から5時間戻る時差の場所(UTC+4)へサマータイムをまたいで7時間で移動すると、出発したときから日本の時計を見ていなければ時計上は2時間後に到着したように見えるが、日本の時計から見れば同時刻に到着したように見える。

まず、これが理解できない、特に自分が殊更に理解できないのではなく当たり前に理解できないものだと思うのであれば、サマータイムには断固として反対すべきである。 日本に関わる全ての人がこれを理解して新しい時間を取り扱うよりほかにないのだから。

解説すると、2020-07-31 22:30:00 JSTにアルメニアに向けて7時間で到着する直行便(存在しないが)で出発したとすると、 到着するのは2020-08-01 00:30:00 に到着するが、日本の時刻は2020-08-01 07:30:00 JDTになっている。 2020-08-01 00:30:00はJSTでもJDTでも実際に訪れることはないのだが、2020-07-31 22:30:00 JST = 2020-08-01 00:30:00 JDTである。なので、出発時刻をJDTで見れば同時刻に到着している。

さらにサマータイムをまたいで帰ると今度は16時間後に到着…するわけではなく、12時間後に到着する。

2時間

実はDSTは1時間と書かれていたりして、「サマータイムが2時間」なんていうのは想定外である。 そのため、DSTをフラグ管理していて、DSTが真である場合標準時に1時間を足す、という処理は結構一般的だったりする。

これを2時間のサマータイムなんて導入すると、世界中が大迷惑である。

しかもだ。 JDTはすでにある。

日本でも4年間ほどサマータイムが導入されたことがあり、そのために日本の時刻情報としてこの4年間JDTが運用された記録が残っている。

% date --date="70 years ago"
Thu Aug 19 18:54:05 JDT 1948

なのでJDTは使うことができず、新しい名前をつける必要があるのだが、 命名規則から外れた名前を、しかもサマータイム用に作る必要が生じる。

2年間のために世界中に恒久的に維持・管理しなければいけない複雑なデータをひとつ増やすわけだ。 迷惑も甚だしい。

単純に作業するだけでも経済損失は日本円換算で何十億、何百億では済まないだろうとは思う。

TZのないデータフォーマット

YAMLの場合

% date; ruby -r yaml -e 'puts YAML.dump Time.now'
Sun Aug 19 16:27:15 JST 2018
--- 2018-08-19 16:27:15.749864798 +09:00
...

日のみだとRubyはTimeZoneのないDateクラスを使用する。

% ruby -r yaml -e 'pp YAML.load(ARGV[0])' "ts: 2018-08-19"
{"ts"=>#<Date: 2018-08-19 ((2458350j,0s,0n),+0s,2299161j)>}

日時のみの場合はUTCとみなす

% ruby -r yaml -e 'pp YAML.load(ARGV[0])' "ts: 2018-08-19 05:00:00"
{"ts"=>2018-08-19 14:00:00 +0900}

TZをつけるとちゃんと認識してくれる。

% ruby -r yaml -e 'pp YAML.load(ARGV[0])' "ts: 2018-08-19 05:00:00 +9" 
{"ts"=>2018-08-19 05:00:00 +0900}

JSTだとは認識していない(タイムゾーンでなく時差で認識している)けれど、通常は問題ないはず。 リアルタイムでカウントする場合は問題だけれども、Rubyのレイヤーではそれはその瞬間に異なる時差が適用されるはずだから。

人間が書いたデータに必須でもないのにタイムゾーンが書いてあるなんてほとんど見たこと無いけれど。

日時情報はあるけれどタイムゾーンがないデータを持っているもので最も使われているのはExcelである

Excelマクロで提示処理、あるいは時刻に基づく集計処理をしている皆さん。 ご愁傷さまです。

AMD APU (A10-7870K Godavari) * Radeon VCE * AMDGPU * VA-API * ffmpeg

理屈としては解説してきたものの、実際にVA-APIでVCEを叩いたことがなかったのでやってみた。 ついでなので、qsvやnvenc、あるいはlibx264などでも応用できるウェブカメラとスクリーンキャスティングの話もしておく。

基本的な手順は単純で、AMDGPU(事実上現在唯一となったAMDビデオドライバ)を選択した上で、 VA-APIを有効にし、VA-API経由でエンコーディングを行う。

注意点としては古くなったプロプライエタリドライバ(Catalyst)ではなく、AMDGPUドライバーを使用することと、 MESA VAドライバを使用することである。

RadeonはVA-API/VDPAUの両方をサポートしており、NvidiaのようにVDPAUドライバをインストールし、VA VDPAUアダプタを使用するという方法(libva-dvpau-driver)も成立してしまう。 しかし、この場合は動作しなくなるので、必ずMESA VAドライバ(libva-mesa-driver)を使用する必要がある。

その上で基本はこんな感じ。 (Arch/ManjaroのffmpegはVA-APIを含んでいるのでffmpeg云々の話はない)

$ ffmpeg -vaapi_device /dev/dri/renderD128 -i source.avi -vf 'format=nv12,hwupload' -c:v h264_vaapi -qp 24 -c:a copy outfile.mp4

ffmpeg上でのVA-APIは公式にドキュメントがある

ソースファイルがビデオアクセラレーションが効くのであればUVDをVA-API経由で叩いてデコードすることもできる。

$ ffmpeg -vaapi_device /dev/dri/renderD128 -hwaccel vaapi -hwaccel_output_format vaapi -i source.avi -vf 'format=nv12,hwupload' -c:v h264_vaapi -qp 24 -c:a copy outfile.mp4

ところがこれではうまくいかなかった。 一件成功しているように見えるのだが、フレームの全くないビデオファイルが出来上がってしまい再生できない。

多分、新しいRadeonだとそんなことはないのだろうと思うけれども、Godavari(2015年)のR7グラフィックスでは問題があったため、-profile 578を指定すると解決した。

$ ffmpeg -vaapi_device /dev/dri/renderD128 -i source.avi -vf 'format=nv12,hwupload' -c:v h264_vaapi -profile 578 -bf 0 -qp 24 -c:a copy outfile.mp4

ウェブカムからキャプチャする場合はエンコードのみ、入力はv4l2:

$ ffmpeg -vaapi_device /dev/dri/renderD128 -f v4l2 -i /dev/video0 -vf 'format=nv12,hwupload' -c:v h264_vaapi -profile 578 -qp 24 outfile.mp4

さらにマイクをPulseAudio経由で拾う場合。音声は192k AAC:

$ ffmpeg -vaapi_device /dev/dri/renderD128 -f v4l2 -i /dev/video0 -vf 'format=nv12,hwupload' -c:v h264_vaapi -profile 578 -qp 24 -f alsa -i pulse -c:a aac -b:a 192k outfile.mp4

X11のスクリーンキャスティング 音声つき。 A10-7870Kだとh264_vaapiは34fpsくらいなので、60fpsはフレーム落ちばかりになる。30fpsも無理で24fpsがドロップしない限界。 ultrafastにした場合は30fpsは可能だけれど60fpsは無理。

$ ffmpeg -vaapi_device /dev/dri/renderD128 -f x11grab -framerate 24 -video_size 1920x1080 -i $DISPLAY -vf 'format=nv12,hwupload' -c:v h264_vaapi -profile 578 -qp 24 -f alsa -i pulse -c:a aac -b:a 192k outfile.mp4

ultrafastで30fps:

$ ffmpeg -vaapi_device /dev/dri/renderD128 -f x11grab -framerate 30 -video_size 1920x1080 -i $DISPLAY -vf 'format=nv12,hwupload' -c:v h264_vaapi -profile 578 -preset ultrafast -qp 24 -f alsa -i pulse -c:a aac -b:a 192k outfile.mp4

なお、-video_size-iよりも先に指定することが必須である。基本的にffmpegはオプションは順番に厳しい。

300×300のウィンドウを左上から200×200のオフセットで録画する場合

$ ffmpeg -vaapi_device /dev/dri/renderD128 -f x11grab -framerate 30 -video_size 300x300 -i $DISPLAY+200,200 -vf 'format=nv12,hwupload' -c:v h264_vaapi -profile 578 -preset ultrafast -qp 24 -f alsa -i pulse -c:a aac -b:a 192k outfile.mp4

-video_sizeの指定方法は色々あって、公式ドキュメントで解説されている。 なので、-video_size 1920x1080ならば-video_size hd1080とも書ける。

録画領域を表示するには-show_region 1

$ ffmpeg -show_region 1 -vaapi_device /dev/dri/renderD128 -f x11grab -framerate 30 -video_size hd1080 -i $DISPLAY -vf 'format=nv12,hwupload' -c:v h264_vaapi -profile 578 -preset ultrafast -qp 24 -f alsa -i pulse -c:a aac -b:a 192k outfile.mp4

NVENCだとmkvにもできるので音声はFLACやOpusで撮ることもできるのだけど、AMDGPU VA-APIはmp4だけ

使ってみた感想としては、QSVと違ってCPUがあくのは良いけれど、R7の速度自体がCPUと大差ない(libx264でやったのと同程度)であるため、ちょっと微妙かもしれない。 A10でもスクリーンキャスティングしながら配信みたいなことができるというメリットはあるけれども。