ラベル windows の投稿を表示しています。 すべての投稿を表示
ラベル windows の投稿を表示しています。 すべての投稿を表示

2024年6月22日土曜日

TvRock自身によるACPI Wake Alarmの遅延補正アプリケーション

 TvRock.exeに直接スリープからの復帰(レジューム)時刻の補正を行ってもらうアプリを作り、RTMfTと名付けてgithubに公開しました。

スリープ開始時刻から録画のために復帰する予定時刻が3.5時間以上未来の場合はACPI Wake Alarmによる復帰が行われるものと見なして補正処理を行います。
なお、補正時間の計算式の根拠については先の記事をご覧ください。補正を行った場合は補正内容がログに出力されます。

おまけ機能としてTvRock頓死監視機能を付けました。まれに番組表から予約操作などをすると頓死することがあるので、手動で再起動するのが面倒な方向けですが、まず使用する必要はないとは思います。

本プログラムはDLLインジェクションという手法を用いて動作中のTvRock.exeに対してパッチを当てます。
そのため、ほかの補正用プログラムを常時起動しておくことが不要になり計算機資源の省力化を達成し、意識高い系用語でいうところのSDGsへの貢献ができるという欺瞞に満ちた満足感を得られる可能性があります。

制限事項として、このプログラムは表題のとおり、ACPI Wake Alarmによってスリープからの復帰(レジューム)を行っても遅延しない環境では復帰時刻が正常よりも早まってしまいますので使用しないでください。

また、採用した手法により、アンチウィルスソフトによる誤判定を受ける可能性が想定されます。判定の正誤はソースプログラムから自分で判断してください。判断できない場合はTvRock.sch使用版を使うという手段があります。

なお、動作可能OSはWindowsVista以後です。

先に作成したプログラムだとtvrock.schの監視が必要だし、そもそもTvRock.exe自身が補正すれば話は簡単じゃないかと思い、改めて作成しました。

基本機能の実装そのものはすぐ終わっていたのですが、8日間も38度を超える強烈な病気になってしまい、TvRock頓死対策などのおまけ機能の着手が大幅に延期されてリリースが遅れました。

だれにも求められていないアプリの記事で大変恐縮です。

以上、お読みいただいてありがとうございました。

2024年5月24日金曜日

TvRockの予約録画時のスリープからの復帰方法について

 いまさらTvRockかとお呆れのかたもございましょうが、お許しください。

(成果物だけほしい方向け: `tvrock.schから補正'版はこちら、`TvRock単体で動作`版はこちらです)

表題の件ですが、タスクスケジューラとそれ以外があります。そして、今回はそれ以外のほうのお話をさせていただきます。

その前に、まず小ネタから。

0.9t8ではTvRockDTV.x64.dllがないためにTvTestとかTSTaskの64bit版が動かないとお嘆きの方に。実は0.9u2で生成させたTvRockDTV.x64.dllをそのまま保持しておいて0.9t8に戻せばTvTestやTSTaskの64bit版が使えます。

他は64bit化したものの、TvRockだけはどうにも余人を以て代え難く、しかし0.9u2はTvRockがOSのスリープを(powercfg /requestsoverrideしてしまわない限り)阻止し続けるので使いたくないという方向けのニッチすぎる情報でした。

さて、TvRockで録画を予約するときに「復帰処理をタスクスケジューラで行う」にチェックボックスを入れていなくてもきちんとスリープから復帰して録画が開始されますが、powercfg /waketimersを行ってもスリープ状態の解除タイマーはありません。

以前から気になっていたのですが、今回こんなネタを知ったのでスリープからの復帰時間の補正をするツールを書くついでにだいたい見当はつくけど確かめてみようということでghidraを用いてバイナリを紐解いてみました。

思った通りWM_POWERBROADCASTを受けたときにwParamがPBT_APMSUSPENDのとき、つまりスリープに入る直前にSetWaitableTimerして、レジュームした直後(PBT_APMRESUMEAUTOMATIC)にCancelしてました。

そのため、普段はpowercfg /waketimersを行っても見えません。普段は登録されていないのですから当たり前です。

ここで、先のACPI Wake Alarmによる遅延のために何とかしたいとする場合は、予約前の準備時間以上スリープしないようにシステムを設定しておくのが無難でしょう。

あえてプログラムを書こうという場合、いくつかアプローチがありますが、私はtvrockと同様にWM_POWERBROADCASTでPBT_APMSUSPENDを受けたときにtvrock.schから現在時刻からの直近で4時間以上未来の録画予約があれば予想遅延時間を引いた時刻でwaitable timerを設定しています。この方法だとPBT_APMSUSPENDをもらってから2秒以内にすべてを終わらせる必要があるのでCで書きました

でもこれは物好きだからやっているだけで、普段からtvrock.schを監視しておいて、変更されたらschtasksコマンドやpowershellを使ってタスクスケジューラ経由でスリープを解除させる方法が簡単だと思います。TvRock.exeではTvRock.schを開く際はCreateFile()で排他しているので書き込み中にリードしたりといった事故は起きないと思います。

ちなみにtvrock.schのSTARTはunix epochなJSTです。

tvrockを卒業することになるかと思いきや、解決策が見つかってしまったのでまだまだ使い続けることになりそうです。やっといてなんですが、もう録画してまで見るものがないのでした。

最後で恐縮ですが、TvRock, TvTest, RecTest, TSTask, Spinel, BonDriverProxyEx, BonDriver_PT3-ST, pt2wdm の作者様方のみならず、多くの関係者の方に心から感謝しています。

以上、このような駄文を読ませてしまい、誠に申し訳ございませんでした。

ACPI Wake Alarmによるスリープからの復帰における遅延時間のデータ (ASUS PRIME B760M-AJ D4)

 表題の件ですが、Windows10や11で使用したときにn時間以上スリープさせた場合、ACPI Wake Alarmによってレジュームすると予定時刻より遅れて復帰するということを知りました。

いやあ、全然知りませんでした。11年ぶりにPCを新調したらこんな落とし穴があったなんて。

先達はあらまほしき事なり、というわけで「アルチーナの魔法の島」さんの「Intel 300シリーズチップセット以降のスリープ解除遅延問題」を読ませていただきました。大変有用な情報をありがとうございます。

UFEIでレガシーなRTCを選べれば回避できるとのことですが、当方がWindows11で運用しているASUS PRIME B760M-AJ D4には項目がありませんでした。

そこで、まずはスリープからACPI Wake Alarmによって復帰というかレジュームというか起床したケースをPower-Troubleshooterが記録したイベントログから拾って表にしてみました。当方の環境では最低でも一日に一回必ず走るジョブがあるので、スリープ時間が一日以上のデータはないのが残念です。

実スリープ
時間
期待スリープ時間遅延秒数遅延秒/時
10:44:1210:39:3028226.45817045
12:23:3812:18:0733126.90631562
6:53:4106:50:4917225.12069455
9:57:0109:52:4126026.3209696
5:38:4205:36:2613624.25443377
14:25:1814:18:4938927.1769295
8:56:1908:52:2823126.02979842
4:33:1104:31:2810322.76522593
9:10:3209:06:3423826.1267305
18:33:0918:24:4150827.59161751
6:46:1106:43:2316824.98863777
7:51:0707:47:4720025.65290198
9:03:2108:59:2723426.02650848
10:44:2010:39:3728326.54715064
5:56:1005:53:4514524.59363958
7:07:2807:04:3017825.1590106
4:32:4204:30:5810423.02866281
4:33:4504:32:0110422.93977085
4:30:2504:28:4310222.77491782
4:57:3604:55:4011623.54002255

遅延秒数とあるのは期待スリープ時間と実スリープ時間との差分で、「本来復帰すべきだった時刻よりもどの程度遅れたか」を計算した秒数です。確かに遅れまくってます。

9時間スリープさせると3.85分の遅刻で、18時間33分では8.46分遅刻する、と。

表だけだとよくわからんので、googleさんにグラフ化してもらいました。

ACPI Wake Alarmによる復帰遅延秒数グラフ

グラフの右側の目盛りは左側の目盛りが合計の遅延秒数で、右側は1時間当たりの遅延秒数です。

先の先達のブログ様では3.5時間以上の場合にACPI Wake Alarmで起床するようになるという情報でしたが、当方でも4時間未満の起床は起床させたプロセスが正しく記録されていました。

一方、一時間おきに25秒程度づつ遅延するということでしたが、こうしてみると、私のケースでは一時間当たりの遅延秒数は、スリープ時間の合計時間によって放物線を描いていて少々異なるようです。

遅延秒数のほうはきれいに値が出ているので、xをスリープから復帰する時刻までの秒数、yを遅延秒数とすると、遅延時間を計算する式はだいたいy=0.008x-27.5くらいでよさそうです。

y切片がマイナスなのですか。長時間のスリープ時はホニャララするとかしないとかの省電力がらみなんでしょうか。でも遅刻はまずいんじゃないかなあ、と思うのは日本人だからでしょうか。

誰にも役に立たない情報ですが、まずは以上です。

ここまでお読みいただいてありがとうございました。

2024年4月18日木曜日

ファイルサーバ(Sambaサーバ)がWindowsから見えないよ(いまさら)

 前回の記事に関連して、いまさら言うような話でもないのですが、Windows7や8からアップグレードを重ねてWindows11まで来てしまったりすると気づかなかったりしますし、まあちょうどよい機会ですのでメモしておきたいと思います。

 Windows10の1709あたり以後、SMB1.0がデフォルトで廃止されたためLinuxやFreeBSD上で稼働しているsambaサーバがWindowsのエクスプローラの「ネットワーク」フォルダ(ややこしい)に表示されなくなったりします。

よっぽど古いNAS製品でもない限り、sambaのバージョンが3.5(おおよそ13,4年前です)以上ならSMB2以上に対応していますから、あくまでもエクスプローラの「ネットワーク」フォルダに表示されないのが問題なので、ファイル共有自体にはSMB1.0は不要です。

で、SMB1.0が入っていないとなぜエクスプローラの「ネットワーク」フォルダに表示されないのかというと、NETBIOS over TCP/IP(NBT)という仕組みでサーバの名前をばらまいたり受け取ったりしているのですが、その仕組みがSMB1.0と抱き合わせなので、SMB1.0を削除すると名前を受け取ったりできなくなります。

sambaはNBT以外で名前を解決する機能は持っておらず、別のプログラムが必要になります。

そこで、Windows VistaからはNBTとは別の名前の解決手段も有効になっていますので、例えばWSDという機能でもってSambaサーバが稼働しているマシン名をばらまくとめでたくSMB1.0なしで(NETBIOSを使わずに)エクスプローラの「ネットワーク」フォルダにsambaマシンが表示されるようになります。

ですので、sambaマシンでWSDをしゃべるデーモンを稼働させればいいわけです。

なお、そもそもエクスプローラの「ネットワーク」フォルダに表示されなくたっていい場合は、ネットワークドライブを割り当ててしまえばいいだけです。
そのほかにも、たとえばショートカットを新規作成し、その「リンク先」に

 \\192.168.x.x\shared_folder

というようにsambaマシンのIPアドレス(hostsやDNSが正しく設定されているなら名称でもOK)を与えてやれば、それ以後はいちいち手入力しなくても共有フォルダが開けます。

さて、sambaが稼働しているサーバがLinuxかFreeBSDだった場合(Windowsだった場合はそもそも必要はありませんよね)、pythonで記述されたWSDデーモンC言語によるそれがあるのですが、python版なら様々なLinuxディストリビューションやFreeBSDでパッケージ化されているので導入が楽だと思います。

python版で一点だけ注意が必要なのは、UbuntuやCentOSなど、使用しているディストリのカーネルオプション次第ではIPv6を使用するとエラーが出ます。その際は起動オプションで明示的にIPv4だけで稼働するよう指示(-4)を与える必要があります。また、WSDで名前を案内したいLANに接続されたネットワークインターフェース名(enp2s0やeth0など)も明示的に指定(-i)する必要があります。
いうまでもありませんが、ドメイン(-d)orワークグループ(-w)、および名前(-n)の指定は必要です。

たいしたことではないのに長くなってしまいました。

以上です。ここまでお読みいただいてありがとうございました。

2024年4月10日水曜日

タスクマネージャ上のCPUの「速度」

 タスクマネージャでCPUの乱の下部に、こんな感じで表示される「速度」。

黄みどり色のまるで囲ったところです。
(ピンクのまるで囲った「使用率」については前回の記事で触れたところです。)

この「速度」、CPUのコアがいっぱいあって、しかもコアごとに全部周波数が違うってえのにどこの「速度」なんでしょうね、と。

実際にはコアごとの平均らしいです。

で、パフォーマンスカウンタの「Processor Information」に「% Processor Performance」というのがあって、_Totalのインスタンスから平均値を100倍した値(percentだから)が取得できます。

何に対してのパーセンテージなのかというと、茶色のまるで囲んだ「基本速度」です。
「基本速度」をプログラムから取得するなら、たとえばパフォーマンスカウンタで取得するなら「Processor Information」の「Processor Frequency」を取得するか、WQLでWMIから取得するなら、
SELECT MaxClockSpeed FROM Win32_Processor;
とかで取得して1000で割ります(取得できる値がMHz単位なので)。

で、
「% Processor Performance」/ 100 *「Processor Frequency」/ 1000

という式を計算すると、黄緑のまるで囲った値(GHz)が得られます。

自力でこれらを計算する場合はintelの場合はコアごとにIA32_APERFとIA32_MPERFとTSC周波数とで何とかするのは見当がつきますがTSC周波数の計算も面倒だし、そもそも今はやりのAMDとかになるとさっぱりわかりません。FreeBSDやLinuxのcpufreqのソースが参考になるのかな。

それだけです。
ここまでお読みいただいてありがとうございました。

2024年4月3日水曜日

WindowsでのCPU使用率の計算について

 割と知られていないようなので一応。

実はWindows界隈ではCPUの使用率の計算に二派閥あったりします。
Windows8の時代から始まって、Windows11な現在でも続いています。

一つはタスクマネージャで表示されるCPU使用率です。CPUが処理に費やした時間ではなくて作業量で使用率を計算しています。作業量を評価する基準にCPUのベースクロックを意識しているので、ベースクロックを超えて動作するターボブースト機能を搭載した近年のCPUだと高負荷時には100%を超えます。
しかし計算結果が100%を超えても表示上は100%に丸めちゃっているので、見た目だけは矛盾が出ないようになっています。

もう一つは、SysinternalsのProcess Explorerなど、管理者向けツールなどでよく使われている方法です。これはWindows7まではタスクマネージャもこの方式でした。この派閥は実時間に対するCPU時間で計算しますので、もちろん100%を超えなくなり、タスクマネージャよりも低いCPU使用率が表示されます。

とりあえず、なぜタスクマネージャのCPU負荷の値があんな高い数値なの?という疑問の答えがこれですよ、ということで。

なぜそんな面倒なことをタスクマネージャがしはじめたかのかというと、当時動的にクロックが可変(speedstepとかもう覚えてないでしょ)にできるCPUが普及したことによって、クロック数が下がったまま動作しているマシンと下がっていないマシンではCPUの使用時間が同じだからって仕事量は違うよね同じ負荷とは言えないじゃん!という考えに基づいています。

一方でクロックが落ちてようがなんだろうが、実際にかかった時間との比率こそボトルネックを探すのに便利だろ!クロックは別に監視しとけばいいだろ!!という派閥もあって、それがProcess Explorerなどのツールです。

それぞれごもっともだし、たいていの場合はどちらの方法でもシステムの負荷の監視には役立つと思いますが、違いを知っておけばボトルネックの解消時の参考になるかもしれません。

ご参考: Powershellやプログラムでタスクマネージャと同じ計算結果を得るにはパフォーマンスカウンタ( "Processor Information" の "% Processor Utility" )を参照すれば取得できます。
Process Explorer方式の方法は上記以上にAPI、WMI、パフォーマンスカウンタなどなどから容易に取得可能で、主にCPU負荷の取得という記事があったらこの方法ばかりだと思いますので特に触れません。

PCを新調したのでLinuxサーバからPCを監視する自家製エージェントを仕込む際にふと思いたって記事にしてみました。

ここまでお読みいただいてありがとうございました。

2024年3月27日水曜日

Hyper-Vを入れてないままWindows subsystem for Linuxの2じゃないほうをインストールしたい。

なんだか巷ではWSL2 が人気みたいです。

「みんなだいすきWSL2」じゃなくて、私はWSL1がだいすきなので、Windows11にWSL2からのダウングレードではなく最初からWSL1を直接インストールする方法をメモします。

まず「Windowsの機能」画面で「Linux用Windowsサブシステム」をインストールします。


実は当記事はHyper-Vにチェックを入れてないままWSL1を入れるためのメモです。そのため、Hyper-Vにチェックされていません。

さて、OKボタンを押したら、次は昔と違って勝手にUbuntuが入ったりしないので、自分で好きなDistroを入れなくちゃいけません。

で、wsl --install -d Ubuntu とか実行すると、次のようなエラーが出て怒られた挙句にインストールに失敗したりします。

Installing, this may take a few minutes...
WslRegisterDistribution failed with error: 0x800701bc
Error: 0x800701bc WSL 2 ???????????? ??????????????????????? https://aka.ms/wsl2kernel ?????????


Press any key to continue...

とか言われます。
あー夢の21世紀でもいまだに多国語対応に失敗してても気にしない。

これはハイパーバイザがないからですから、さっきの画面でHyper-Vを入れればいいんですが、私は入れたくないんです。

そこで、wsl --installをする前に以下のコマンドを入力します。

wsl --set-default-version 1 

その後改めて、wsl --install -d Ubuntu を行うとめでたくWSL1をHyper-Vを入れずにインストールすることができます。

それだけです。

2022年11月30日水曜日

ケーブル一つでもシステムにとってかけがえのない存在なことを再認識させられました

 現在使っているPCは、かれこれ足掛け10年の間、週一回程度にビデオカードの差し直しをしないと正常に使えなくなるという爆弾を抱えているものの、ハードウェア的には仮に起きても本当に些細なトラブルばかりで(PT3が壊れた!と思ったら差し直したら直ったとか、20年以上愛用のPS/2キーボードの右CTRLキーがついに押下すると元に戻らなくなったりとか、そんなのばっかりで)びっくりするほど安定(VGAから目を背けつつ)しているのですが、やっとネタになりそうなハードウェアトラブルが発生したので記録する次第です。

まず、HDDのGドライブとして運用中の、このPCの他のハードディスクの中身をバックアップするだけの用途のドライブがあると思ってください。

そのGドライブが、ある日、アクセスされっぱなしになっていることに気づきました。

バックアップ専用ドライブですから、バックアップ中以外にBUSYになっている時点でおかしいのですが、実際にGドライブにエクスプローラーからアクセスしてみると妙に遲いものの各ファイルには正常にアクセスできます。

とりあえず、Windowsにおける最強のトラブル解決手段であるところのリブートを行ってみました。すると、特に問題なさそうな様子をしています。

しかし、実際には何か異常が起きたからこそ奇妙な挙動をしたのですから、原因を知りたい。

そこで、これは10年近い年季のHDDがいかれかけでバッドセクタが出たのかな?と考えてSMART値をチェックしてみました。しかし、SMARTを軽く見てみても、代替処理済みのセクタ数もセクタ代替処理保留中のセクタ数も回復不可能セクタ数もゼロのままで問題がなさそうです。

そこで、念のため chkdsk /r G: をかけてバッドセクタを探させることにしました。

すると、ステージ4である程度の個所でディスクへのアクセス頻度は高いけれど進捗がぱったりと進まなくなる状況になりました。

この辺は場数をどの程度踏んでいるかによるのですが、大容量HDDだから進捗が進まないのか、トラブルが起きているから進まないのかの見極めが必要となります。
ここでは後者、何らかのトラブルが発生しているためにchkdsk が進まないと判断して中断させようとしましたが、CTRL+CもCTRL+Breakも効果なく、やむなくコマンドプロンプトごと終了させました。

当然chkdskによってG:ドライブはアンマウントされているので再マウントしようとしましたがうまくいきません。焦っていると「すわ、壊したか!」と慌てるところですが、ここは、理屈ではここは単純にリブートで行けるはずと踏んでリブートするとG:ドライブが復活しました。

そこで改めてG:ドライブのファイルを閲覧したりすると、やっぱり奇妙に重い。今のところ何の解決にもなっていないことは明らかですので、SMART値を精査することにしました。

すると、「UltraDMA CRC エラー数」が膨大な値になっていることが分かりました。

これらをWindowsのイベントログを見ると
ソース:disk
イベントID: 153
「ディスク 1 (PDO 名: \Device\00000031) の論理ブロック アドレス 0x600870 で IO 操作が再試行されました。」
※論理ブロックアドレスは一定ではありません

というログが記録されています。

以下のようなログもありました
「トランザクション ログへのデータのフラッシュに失敗しました。VolumeId: G:、DeviceName: \Device\HarddiskVolume7 で破損が発生している可能性があります。」
以下HDD型式などの情報

ともあれ、SMART値が示すところでは、HDDとの通信がうまくいかないでエラーが起きてることを示しているので、最悪の場合はマザーボードの故障ですが、まずはSATAケーブルの交換を試してみます。

...正常に稼働するようになりました。

地味だけど重要な仕事をしてくれている伝送路を担うケーブルさんに感謝するためここに記録する次第です。

ここまでお読みいただきありがとうございました。

2022年8月14日日曜日

セキュリティ更新プログラム (KB5012170)が0x800f0922エラーで失敗するのはセキュアブートの設定が原因かもしれません

結論から申し上げますと、私の環境ではセキュアブートをOFFからONに変更するとKB5012170のインストールに成功しました。

その後、再度セキュアブートをOFFに戻しても特に問題なく稼働しています。

結論は以上です。

以下は蛇足です。

さて、当環境では火曜日以来、今月のWindows Updateでこんな問題が発生していました。

週末なので、そろそろ解決しようと思いました。
そこで、そのエラーコード0x800f0922を引き起こしているのは何やつかと尋ねると・・・

何度も失敗していますが、それはすべてKB5012170だそうです。
KB5012170とは何者かと尋ねると・・・

だそうです。

セキュアブートは当環境では無効にしてあるので、まずは有効にしてみることにしました。

そのためにはUEFIの設定を変更する必要があるため、マザーボードごとに用語や手順が異なるかと思いますが、当環境のASRock H87 Pro4を例にしますと、まずはSecure Bootはこんな状態でした。

セキュア起動とか安全起動とか楽しい日本語になっていますが、これらはすべてSecure Bootを意味しています。
上の状態を以下の状態にしてあげます。
上記の画面は、セキュアブートを有効にして、「システムモード状態」を「Setup」から「デフォルト値で初期化した状態」を示しています。

これでセキュアブートが有効になるのですが、保存して再起動する前に、もう一か所だけ確認したほうがいい箇所があります。

それは「CSM(Compatibility Supported Module)」です。CSMはセキュアブートとは「混ぜるな危険」の関係なので、ちゃんとOFFにされているか確認したほうがよいでしょう。

CSMが有効になっている場合、例えばこんな感じになります。
これを、以下のように変更します。
この状態になっていることを確認したら、設定を保存して再起動し、Windowsを起動します。

そして、さっそくWindows Updateをかけてみたところ、
となって、無事KB5012170のインストールに成功しました。
履歴を参照すると、
きちんと「正しくインストールされました」となりました。
なお、よくよく見てみると、驚いたことに、「更新の履歴を表示する」といっているくせに、失敗の履歴はすべて削除されてしまいました。
そんなの履歴じゃないじゃん。
歴史修正主義者マイクロソフトさんなのでした。

おまけ:この問題をイベントビューアで確認すると、次のように記録されています。
インストールの失敗: エラー 0x8024200B で次の更新プログラムのインストールに失敗しました: 2022-08 x64 ベース システム用 Windows 10 Version 21H1 のセキュリティ更新プログラム (KB5012170)。
エラーコードまで違ってんじゃん!!
歴史改ざん主義者マイクロソフトさんなのでした。

さて、愉快なマイクロソフトさんの歴史観はさておいて、当環境の場合は自作のデバイスドライバに私のオレオレ証明書で署名をしています。
セキュアブートの仕事は起動時のドライバやOSのデジタル署名をマイクロソフト以外に許すか許さないかなので、セキュアブートは無効に戻さなくてはなりません。

セキュアブートは一度でも有効にしてWindowsを起動すると、BCD(Boot Configuration Data。Windows Vistaからの伝統で、二進化十進数の事じゃございません)からtestsigning項目がOFFにされてしまいます。
そこで、UEFI画面でセキュアブートを無効にした後、Windowsを起動したら再度testsigning項目をONにしてあげてWindowsを再起動する必要があります。
コマンドは次の通りです。
bcdedit /set testsigning on

以上、どなた様かに役立つかもしれないと思い、記録する次第です。
このような駄文をお読みいただいてありがとうございました。

2020年11月3日火曜日

MoUsoCoreWorkerがスリープを阻害する

ここ数か月か、Windows10がWindows Updateで何かしらシステムがアップデートされて以後、スリープができなくなるという現象に見舞われています。


その際、powercfg.exe /requestsを実行してみると、

C:\WINDOWS\system32>powercfg /requests
DISPLAY:
なし。

SYSTEM:
なし。

AWAYMODE:
なし。

実行:
[PROCESS] \Device\HarddiskVolumeX\Windows\System32\MoUsoCoreWorker.exe
USO Worker

PERFBOOST:
なし。

ACTIVELOCKSCREEN:
なし。

という結果が得られる場合があります。

これは太字のMoUsoCoreWorker.exeがシステムに対して「スリープしちゃイヤン」というお願いをし、それをシステムが聞き入れているという意味です。

「よし、MoUsoCoreWorkerが原因なんだな!ぶっ殺してやる!!」とか言ってタスクマネージャを起動してプロセスをkillする前に、まず試してもらいたいことがあります。


まず、このMoUsoCoreWorkerはWindows Updateの眷族で、Windows Updateとズブズブの関係です(そらそうだ)。

で、Windows Updateでアップデートを行わせたい場合、こいつが出張ってきてWindows Update本体(といえばいいのかな?)が活動中にシステムがスリープしてしまわないように邪魔をする仕事もします。

この時点で、新しいアップデートファイルがMS側に用意されていることはMoUsoCoreWorkerにはわかっています。

こういったケースの場合にMoUsoCoreWorkerによるスリープの阻害が発生していることを数回確認しました。


なお、これは、「オプションの品質更新プログラムがあります」という表示とは別物です。

重要なので再度繰り返させていただきますが、別物です。

ここからがお笑いなのですが、Windows Update本体はこの新しいアップデートファイルを何日も見つけられないまま最新の状態です」と強弁し続ける場合があります。


つまり、オプション以外のアップデートがあることはMoUsoCoreWorkerにはわかっているが、それをユーザに提示できず、かつWindows Update本体がそのアップデートをダウンロードしてシステムを更新できないという状況が現出します。


これは、システムを再起動しても、「設定」画面の「更新とセキュリティ」の「Windows Update」画面で「更新プログラムのチェック」ボタンを連打しても変わらないことがあります。

そうなると、ユーザには何が原因なのか外見からはわかりません。

憎きはMoUsoCoreWorkerただ一人、きゃつを封じるためにはどんな手でも使わなくては!という短絡的な思考に走っちゃいけません。


まず「サービス管理ツール」を開いて次の二つのサービスを再起動してみてください。

サービス名表示名
UsoSvcOrchestrator Service の更新
wuauservWindows Update

すると、あらびっくり、これまで見えていなかった更新ファイルが急に見えるようになっちゃったりするのです。

(繰り返しますが、これは「オプションの品質更新プログラム」ではありません。)


そして、その見えていなかったファイルがいったん見えるようになりますと、Windows Updadeを介して更新できるようになります。

そして、更新すると、MoUsoCoreWorkerはおとなしくなり、以後はスリープを阻害することがなくなります。


私の経験では、これまで3回MoUsoCoreWorkerが出張ってきていたうち、3回ともこの方法で問題を解消できました。


以上、どなたか様の参考になれば幸いです。

ここまでお読みいただき、ありがとうございました。


2019年4月28日日曜日

ffplay+ffmpegでその場で二つの動画の右半分と左半分を同時に再生する方法

この記事では、エンコード前の動画とエンコード済みの動画の画質の差や、同一の動画を違う形式でエンコードしたときの画質の差を、それぞれの動画を単純に左右に並べるのではなく、動画を右半分と左半分に分割して比較したい場合を想定しています。
ほら、テレビショッピングとかでよくあるじゃないですか。
同じ人の顔を画面上で半分に割って、左半分はナントカクリームを塗らなかった場合、右半分はナントカクリームでお肌ツヤツヤってやつを見せるアレ。
ああいうイメージです。

さて、画質の差は結局のところ数値化などは意味がなくて、見ている本人が納得できるかできないか、という点に尽きると思います。

まず、最も簡単な比較方法は動画プレイヤーを二つ並べて再生することです。
が、同一シーンを同時比較するためには、神業のような再生ボタン押下処理が必要になります。神ならぬ身の上、凡人にはとてもできません。

また、もしできたとしても、元画像が1440x1080だったりFHDだったりした場合、素直に横に並べると大きすぎて位置合わせが難しかったりします。だからと言って普段見るはずのサイズでチェックしないと検査になってないので、縮小等リサイズして比較するのは避けたいところです。

ということで、表題の件のように、二つの動画の左半分と右半分を同時に再生して、すぐに官能検査してみようというのが今回の目論見です。

これを行うのに必要なプログラムはffmpegとffplayだけです。
ffplayはvlc playerでも代替できます。

まず結論として、コマンドライン一式をWindowsのバッチファイルとして作成すると以下になります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
@echo off
setlocal
set SRC1=%1
set SRC2=%2
 
if not defined SRC1 goto invalidargument
 
REM remove doublequotation
set SRC1=%~1
 
if "%SRC1%"=="/?" goto help
if /i "%SRC1%" equ "/h" goto help
if /i "%SRC1%" equ "/help" goto help
if "%SRC1%"=="-?" goto help
if /i "%SRC1%" equ "-h" goto help
if /i "%SRC1%" equ "-help" goto help
 
if not defined SRC2 goto invalidargument
 
REM remove doublequotation
set SRC2=%~2
 
if not exist "%SRC1%" (
  echo エラー: %SRC1%がありません。
  goto help
)
if not exist "%SRC2%" (
  echo エラー: %SRC2%がありません。
  goto help
)
 
REM オーバーレイ文字列用ファイル名.拡張子抽出
set SRC1FNAME=%~nx1
set SRC2FNAME=%~nx2
REM 念のためアポストロフィを除去
set SRC1FNAME=%SRC1FNAME:'=%
set SRC2FNAME=%SRC2FNAME:'=%
 
REM 時間指定パラメタの記述場所判定用拡張子抽出
set SRC1EXT=%~x1
set SRC2EXT=%~x2
 
set STARTTIME=%~3
set ENDTIME=%~4
if defined STARTTIME ( set STARTTIME= -ss %STARTTIME% )
if defined ENDTIME ( set ENDTIME= -to %ENDTIME% )
 
set FFMPEG=ffmpeg.exe
set FFPLAY=ffplay.exe
set FFPROBE=ffprobe.exe
 
REM ぢでじ用パラメータ
set ASPECT=16:9
set MOVIE_WIDTH=1440
set MOVIE_HEIGHT=1080
set FPS=29.97
set CENTERLINE_WIDTH=2
 
if exist "%FFPROBE%" call :trygetparams
 
REM ファイル名表示用フォントファイル(要エスケープ: コロン及び¥)
set FONT=C\\:/WINDOWS/fonts/meiryob.ttc
REM 長いファイル名対策のため左右にテキストを流す
set DRAWTEXT_FNAME_OPT=fontfile=%FONT%:fontsize=24:fontcolor=darkgreen:shadowx=1:shadowy=1:x=w-mod(n*4\,w+tw):y=h-th-24:
REM 再生中動画のPTSとフレーム番号
set DRAWTEXT_PTS_OPT=fontfile=%FONT%:fontsize=12:fontcolor=white:shadowx=1:shadowy=1:text= pts\\:%%{pts\\:hms} %%{pts\\:flt} frame\\:%%{frame_num}
 
 
REM 再生開始及び終了時間指定
set DURATION1= %STARTTIME% %ENDTIME%
set DURATION2= %DURATION1%
REM (ffmpegではmpeg2tsは前置指定での時間指定不可なので引数の位置を後置する)
set DURATION_POSITION=PRE
if /i "%SRC1EXT%" equ ".ts" set DURATION_POSITION=POST
if /i "%SRC2EXT%" equ ".ts" set DURATION_POSITION=POST
if %DURATION_POSITION%==POST (
  set DURATION_POST=%DURATION1%
  set DURATION1=
  set DURATION2=
  echo  *警告: mpeg2tsのため指定時刻への頭出しに馬鹿みたいに時間がかかります*
  pause
)
 
set /A HALF_WIDTH=%MOVIE_WIDTH%/2
set /A RIGHT_HALF_X=%HALF_WIDTH%+%CENTERLINE_WIDTH%
 
set FILTER_OPT=^
    nullsrc=size=%MOVIE_WIDTH%x%MOVIE_HEIGHT%,fps=%FPS% [canvas]; ^
    [0:v] crop=%HALF_WIDTH%:%MOVIE_HEIGHT%:0:0,fps=%FPS%, ^
          drawtext=%DRAWTEXT_PTS_OPT% , ^
          drawtext=%DRAWTEXT_FNAME_OPT%:text=%SRC1FNAME% [left]; ^
    [1:v] crop=%HALF_WIDTH%:%MOVIE_HEIGHT%:%HALF_WIDTH%:0,fps=%FPS%,^
          drawtext=%DRAWTEXT_PTS_OPT% , ^
          drawtext=%DRAWTEXT_FNAME_OPT%:text=%SRC2FNAME% [right]; ^
    [canvas][left] overlay=x=0:shortest=1 [lefthalf]; ^
    [lefthalf][right] overlay=x=%RIGHT_HALF_X%:shortest=1
 
%FFMPEG% %DURATION1% -i "%SRC1%" %DURATION2% -i "%SRC2%" ^
 -filter_complex "%FILTER_OPT%" -f nut ^
 -aspect %ASPECT% -c:a copy -c:v rawvideo %DURATION_POST% pipe:1 ^
 | %FFPLAY% -i pipe:0 -fflags nobuffer
REM | %FFPLAY% -i pipe:0 -fflags nobuffer
REM VLCの場合は、「 | vlc.exe -」
goto end
 
:trygetparams
REM ffprobeの出力からfilter_complexの各値の取得を試みる
set FFPROBEOPT=%FFPROBE% -i "%SRC1%" -of csv -show_streams -hide_banner
REM ffprobeの結果をcsv形式で受け取り、カンマで分割した10,11,16,28の要素を取得
REM ※ここで直接リダイレクト先とパイプを指定しないとうまくいかないようだ
REM ※おまけにexeのパス名にスペースが入っていてもまともに動かないと来た。
for /f "usebackq tokens=10,11,16,28 delims=," %%a in (`%FFPROBEOPT% 2^>NUL ^| findstr video`) do (
  set MOVIE_WIDTH=%%a
  set MOVIE_HEIGHT=%%b
  set ASPECT=%%c
  set FPS=%%d
)
 
echo   ffprobe結果;
echo     size=%MOVIE_WIDTH%x%MOVIE_HEIGHT%
echo     aspect=%ASPECT%
echo     fps=%FPS%
pause
exit /b
 
:invalidargument
echo エラー: 引数が不足しています
:help
echo 使い方: %0 左半分を表示する動画 右半分を表示する動画 [ 再生開始時刻 [ 再生終了時刻 ] ]
echo         再生開始・終了時刻書式 HH:MM:SS または 秒
:end
endlocal
上記の内容を"左右2分割.bat"とでも名前を付けて保存して実行してください。使い方が表示されます。
ffmpegの特徴は複雑なことができる代償として呪文のような意味不明なコマンドラインを構築しなければならないところですが、今回も例にもれず一見意味不明な呪文が並びます。
おまけに当方の環境の都合で(サーバにはAV機能が皆無なので)Windows用のバッチとしたため、シェルスクリプトに比べて極端にひねくれています。
この両者の強力な組み合わせによって、上記のバッチは心の底からうんざりさせてくれるに十二分の威力を備えています。

さて、バッチの書式とか細かいことは放っておいて、重要なのは画像をそれぞれ左半分だけ、右半分だけ、それぞれ切り出して一枚の画像にすることですが、それをffmpegの -filter_complexオプションで行います。
このパラメータはffmpegの中でも極端に面倒くさいので、冗長になることを恐れずに説明したいと思います。

なお、行末の^(サーカムフレックス)はWindowsのcmd.exeで1行で記述しなければならない行を複数行に分割する記号ですので無視してください。
  1. 88行目のnullsrc=ですが、ffmpegが予め用意している多くの入力ソースの中の一つです。ここではこの上に2枚の動画を重ねてゆくベース動画として利用します。
    パラメータはsizeとfpsを指定しています。
    このバッチではsizeは最終的に出力される動画サイズ(1440x1080)、fpsは29.97が指定されています。これらの値はエンコード元が地デジのtsと仮定しています。ことさらにfpsを指定しているのはffmpegは無指定だと25fpsとして処理するからです。
    そして、このnullsrcから入力して加工(sizeとfps)した動画に対して[canvas]と名付けました。これの名づけルールは自由です。
    ここまででベース動画に対する定義は終わりました。続きがあるためセミコロンをつけます。
  2. 89行目の[0:v]ですが、これは1つ目の入力ソース(動画ファイル)コンテナ中で最初に見つかった動画を表しています。これを名付けたのはffmpegです。これに対してcropとfpsとdrawtextの指定をしています。
    cropは切り抜き範囲を指定します。このパラメータはffmpegのバージョンによって解釈される意味合いが変わるというとんでもない代物なので要注意です。
    バージョン20190426-f857753では切り抜き幅:同高さ:元動画に対する切り抜き開始位置X:同Yの順で解釈されます。
    上記のバッチでは720:1080:0:0として左半分を切り抜いています。言い換えると、1つ目の入力ソースの座標(左から0,上から0)から(幅720,高さ1080)だけ切り抜け、という意味合いになります。
    drawtextパラメータはどちらのファイルを切り抜いたかわかるようにファイル名を動画にオーバーレイするためのものです。
    以上を[left]と名付けました。これも名づけルールは自由です。続きがあるためセミコロンをつけます。
  3. 92行目の[1:v]ですが、これは2つ目の入力ソース(動画ファイル)中で最初に見つかった動画を表しています。これを名付けたのはffmpegです。これに対してもcropとfpsとdrawtextの指定をしています。
    異なるのは切り抜き位置で、上記のバッチでは720:1080:720:0を指定して右半分を切り抜いています。2つ目の入力ソースの座標(左から720,上から0)から(幅720,高さ1080)だけ切り抜けという意味合いになります。
    以上を[right]と名付けました。これも名づけルールは自由です。続きがあるためセミコロンをつけます。
  4. 95行目の[canvas][left]ですが、これは私がつけた名前です。この二つを重ね合わせるため、overlay=x=0:shortest=1というパラメータを指定しています。重ねる順番は左から順に重なります。この例では[canvas]の上に[left]が重なります。
    x=0は[left]を[canvas]上のどこに重ねるのかを指定しています。この場合は重ね合わせのx座標を0、つまり一番左としています。本来はこの記述は不要ですが、次の手順で右側に重ね合わせる際の座標指定との対比を分かりやすくするために付与しています。
    shortest=1はどちらかの動画の再生が終わったら処理を完了するというおまじないです。[canvas]の動画はnullsrc由来でdurationを指定していないので無限に続きますので、[left]の再生が終わったら完了するという意味になります。
    以上を[lefthalf]と名付けました。これも名づけルールは自由です。続きがあるためセミコロンをつけます。
  5. 96行目の[lefthalf][right]ですが、これは私がつけた名前です。この二つを重ね合わせるため、 overlay=x=%RIGHT_HALF_X%:shortest=1というパラメータを指定しています。重ねる順番は左から順に重なります。この例では[lefthalf]の上に[right]が重なります。
    x座標に指定している %RIGHT_HALF_X% の値は722ですので、2ピクセル分(本バッチの環境変数"CENTERLINE_WIDTH=2"として定義しています)だけ右にずれて重ね合わされます。これによって、隙間に[canvas]の地の動画が出ます(緑単色)。これは、左右の動画がどこで分割されているのかを分かりやすくするためにあえて行っています。
    以上でベース動画に右半分と左半分をオーバーレイ出来ましたので続きはありませんのでセミコロンをつけません
    なお、ここでセミコロンをつけますとffmpeg様に激烈に叱られた挙句ABENDしてくださいます。
というようなことをやった挙句、ようやく--filter_complex=オプションにダブルクォーテーションで囲んで指定します。

ここまでで2つの入力ソース(動画ファイル)が合成されているので、これをどこかに吐き出さないと人間様には見えません。
今回はいったんファイル化するのではなく、いきなり再生してしまおうというのが趣旨ですので、ffmpegにもれなくついてくるffplayで直接再生させたいと思います。

そのためのオプションは
-f nut -aspect %ASPECT% -c:a copy -c:v rawvideo pipe:1 | %FFPLAY% -i pipe:0 -fflags nobuffer
となります。

出力フォーマットにnut, ソースのサイズは1440x1080なのでぢでじのアスペクト比16:9を明示的に指定して、音声は再変換せずコピーして動画のコーデックをrawvideoとしてパイプ経由でffplayに出力しています。
vlc playerで再生したい場合は、パイプでつなぐアプリをffplayからvlc -(ハイフン)に置き換えてください。こちらもstdinから読み込んで再生してくれます。
率直に言ってffplayはとても使いづらいので、普段ffplayで動画を見ている人というのも少ないでしょうし、vlcも多くのOSに移植されていますから、vlcで官能試験を行ったほうが普段の視聴環境により近いかもしれません。

出力フォーマットのnutというのは単純にピクセルの羅列を扱えますので、rawvideoな動画を扱う際のフォーマットとして優れています。お望みであればこれをファイルに保存してもいいのですが、大変愉快なファイルサイズになります。

蛇足ですが、上記のバッチの例をお読みになれば上下分割や四分割なんかも簡単にできることはご理解いただけると思いますので、見やすいように改造してみてください。

なお、入力ソース次第では-ssが効かない場合があるとか使用するffmepgのバージョンが違うので挙動が違うとかザラです。
文中にもありますが、本稿は20190426-f857753を対象に記事を書いています。

個人的にはffmpegって強力で頼もしいけど融通は利かないしコロコロ仕様が変わるんで尊敬はするけど近づきたくはないです。

以上、大変くだらない記事で恐縮ですが、ここまでお読みいただいてありがとうございました。

以下、駄文です。
なんでこんなことをしようと思ったのかというと、HandBrakeのバージョンが上がるたびに同じH.265のエンコードパラメータを与えても妙に平均ビットレートが上がってきていて、同じCRF値でも1タモリ倶楽部単位(エンコネタではタモリ倶楽部1話分を当blogの基本単位とさせていただいております)の保存ファイルサイズが100MBほど大きくなってきたのが発端です。

なぜそうなるのかはHanbBrakeのせいなのか、libx265のせいなのかはわかりませんが、同一CRF値を指定してもエンコードされたファイルの平均ビットレートが上がっているのでファイルサイズが大きくなっているのが原因なのはわかったので、そっちが同じパラメータでも挙動を変えようって魂胆なら、こっちはもうCRF値を下げちまえ、もともとCRF値は28という(劣化に厳しい諸兄にはありえない)数値でエンコードしていましたが、今回は大台の30にして比較してみよう、でも画質はどうなるのかな?という素朴な疑問を持ったため、尊敬はするけど近寄りたくないffmpeg様のお力を拝借することにした次第です。

やってみてわかったことは、H.265の低ビットレートにおける再現力の高さです。
確かにわずかに28と30では違うかもしれないが、そもそもいずれもオリジナルのtsとの差ともわずかだ、という官能評価に至りました。
ぶっちゃけ、個人的評価ですがエンコ元先を左右に並べても上下に並べてもいずれも全然気にならないことが確認できました。

HEVC、恐るべし。そんなことをつらつら思う平成最後の日曜日でございます。
駄文までお読みいただき、誠にありがとうございました。