created: 2024-08-30T08:47:16.586Z

CPUあんまりつかわないでタイムラプスでスクリーンを録画する

「集中力が続かない人は、画面のタイムラプスをとってみるようにすると改善のきっかけになったよ」というツイートがあった。

自分もすぐにネットサーフィンをしてしまうので同じことをしてみたい。

どうやるか

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}`;
ホットクックお助けレシピ 予約調理のおかず ; 少ない材料&調味料で、あとはスイッチポン!
[ad] ホットクックお助けレシピ 予約調理のおかず ; 少ない材料&調味料で、あとはスイッチポン!
橋本加名子 (単行本(ソフトカバー))