created: 2024-08-30T08:47:16.586Z
CPUあんまりつかわないでタイムラプスでスクリーンを録画する
「集中力が続かない人は、画面のタイムラプスをとってみるようにすると改善のきっかけになったよ」というツイートがあった。
就職してから、PCに長時間向かっているわりに仕事が進まないので、画面のタイムラプスをとることにしました。後で見返すと、仕事に行き詰まるたびにネットサーフをはじめていることがわかりました。これは「タイムラプスを撮っている」と思いながら仕事をしているうちに改善していきました。
— ロボ太 (@kaityo256) August 29, 2024
自分もすぐにネットサーフィンをしてしまうので同じことをしてみたい。
どうやるか
OBS でキャプチャを撮れるみたいだけれども、そこそこリソース食ってエコじゃないのでスクショを ffmpeg に結合してもらうようにした。
- screencapture コマンドで 10 秒に 1 回スクショを撮影
- 連番で名前をつけておく
- けっこう画像が容量おおきかったので、すこし小さめにリサイズしておく
- ffmpeg で png ファイルを結合
- 連番の画像からフレームレートを指定して動画にしてくれる機能がある
- 文字が読めないくらいにちょっとぼやかす
エコかな
gtime で使用した CPU 時間を確認した。10 分間実行してこれくらいならいいんじゃないでしょうか。
Command being timed: "screenshot-intervally.mjs --minutes 10" User time (seconds): 6.66 System time (seconds): 1.67 Percent of CPU this job got: 1%
gtime の出力をぜんぶ見る↓
Command being timed: "screenshot-intervally.mjs --minutes 10"
User time (seconds): 6.66
System time (seconds): 1.67
Percent of CPU this job got: 1%
Elapsed (wall clock) time (h:mm:ss or m:ss): 10:04.39
Average shared text size (kbytes): 0
Average unshared data size (kbytes): 0
Average stack size (kbytes): 0
Average total size (kbytes): 0
Maximum resident set size (kbytes): 60320
Average resident set size (kbytes): 0
Major (requiring I/O) page faults: 2997
Minor (reclaiming a frame) page faults: 345417
Voluntary context switches: 383
Involuntary context switches: 27577
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 214
Socket messages received: 118
Signals delivered: 79
Page size (bytes): 16384
Exit status: 0
スクリプト
スクリプトは ChatGPT が吐き出してくれた。macOS でしか動かない。
#!/usr/bin/env zx
$.verbose = true;
// `screenshot-intervally.mjs --minutes 10`;
const now = new Date();
const formattedDate = now.toISOString().split("T")[0];
const dir = `${process.env.HOME}/Desktop/timelapse/${formattedDate}`;
// ディレクトリの作成(既に存在する場合はスキップ)
await $`mkdir -p ${dir}`;
const minutes = argv.minutes;
if (!minutes) {
throw Error("no --minutes");
}
// スクリーンショットを撮る関数
async function takeScreenshots() {
const interval = 10 * 1000; // 10秒
let count = 1;
// 現在の時間を基準にする
const endTime = new Date(Date.now() + minutes * 60 * 1000); // n分後の時刻
while (new Date() < endTime) {
// 5桁の連番形式にする
const filename = String(count).padStart(5, "0") + "_orig.png";
const fullPath = `${dir}/${filename}`;
// スクリーンショットを撮影
await $`screencapture -x "${fullPath}"`;
// 解像度を下げる
const resizedFilename = String(count).padStart(5, "0") + ".png";
await $`sips -Z 800 "${fullPath}" --out "${dir}/${resizedFilename}"`;
// 元のファイルを削除(オプション)
await $`rm "${fullPath}"`;
count++;
await sleep(interval);
}
}
await takeScreenshots();
cd(dir);
await $`rm -f output.mp4`;
const dockerFfmpeg = [
"docker",
"run",
"--rm",
"-i",
"-v",
`${dir}:${dir}:rw`,
`-w${dir}`,
"jrottenberg/ffmpeg:latest",
];
const ffmpegCommand = [
"-y",
"-framerate",
"20",
"-start_number",
"1",
"-i",
"%05d.png",
`-vf`,
"smartblur=luma_radius=1:luma_strength=1",
`-c:v`,
`libx264`,
`-pix_fmt`,
`yuv420p`,
"output.mp4",
];
await $`${dockerFfmpeg} ${ffmpegCommand}`;
console.log(`open output.mp4`);
const terminalNotifier = [
"terminal-notifier",
`-title`,
`ScreenshotTimelapse 📸`,
`-message`,
`指定時間のスクリーンショットを取り終わりました`,
];
await $`${terminalNotifier}`;