Archive security camera footage

“Sentry” (a property management app like Verkada) keeps weeks of continuous footage. Raw camera streams are enormous. Compress them for long-term storage without losing useful detail.

API

ittybit video \
  -i https://sentry-app.com/cameras/lobby/2024-03-15-08.mov \
  --codec h265 \
  --quality medium \
  --fps 15 \
  --format mp4 \
  --cloud
const task = {
  input: "https://sentry-app.com/cameras/lobby/2024-03-15-08.mov",
  kind: "video",
  options: {
    codec: "h265",
    quality: "medium",
    fps: 15,
    format: "mp4",
  },
};

const res = await fetch("https://api.ittybit.com/tasks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ITTYBIT_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(task),
});
const data = await res.json();
import requests

task = {
    "input": "https://sentry-app.com/cameras/lobby/2024-03-15-08.mov",
    "kind": "video",
    "options": {
        "codec": "h265",
        "quality": "medium",
        "fps": 15,
        "format": "mp4",
    },
}

res = requests.post(
    "https://api.ittybit.com/tasks",
    headers={"Authorization": f"Bearer {api_key}"},
    json=task,
)
data = res.json()
TASK='{
  "input": "https://sentry-app.com/cameras/lobby/2024-03-15-08.mov",
  "kind": "video",
  "options": {
    "codec": "h265",
    "quality": "medium",
    "fps": 15,
    "format": "mp4"
  }
}'

curl -X POST https://api.ittybit.com/tasks \
  -H "Authorization: Bearer $ITTYBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$TASK"

H.265 at medium quality with 15fps (security cameras don’t need 30fps) cuts storage by ~90%.

CLI

ittybit video \
  -i lobby-2024-03-15-08.mov \
  -o lobby-archive.mp4 \
  --codec h265 \
  --quality medium \
  --fps 15

Extract a specific incident

Pull a 2-minute clip from an 8-hour recording:

ittybit video \
  -i lobby-2024-03-15-08.mov \
  -o incident.mp4 \
  --start 14400 \
  --end 14520 \
  --quality high

Grab a still frame

Extract a frame at the moment of interest:

ittybit image \
  -i lobby-2024-03-15-08.mov \
  -o incident-frame.jpg \
  --start 14410 \
  --width 1920

Archive to S3

Compress and write directly to cold storage:

ittybit video \
  -i s3://camera-raw/lobby/2024-03-15-08.mov \
  -o s3://camera-archive/lobby/2024-03-15-08.mp4 \
  --codec h265 \
  --quality medium \
  --fps 15 \
  --format mp4 \
  --cloud
const task = {
  input: "s3://camera-raw/lobby/2024-03-15-08.mov",
  kind: "video",
  options: {
    codec: "h265",
    quality: "medium",
    fps: 15,
    format: "mp4",
  },
  output: "s3://camera-archive/lobby/2024-03-15-08.mp4",
};

const res = await fetch("https://api.ittybit.com/tasks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ITTYBIT_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(task),
});
const data = await res.json();
import requests

task = {
    "input": "s3://camera-raw/lobby/2024-03-15-08.mov",
    "kind": "video",
    "options": {
        "codec": "h265",
        "quality": "medium",
        "fps": 15,
        "format": "mp4",
    },
    "output": "s3://camera-archive/lobby/2024-03-15-08.mp4",
}

res = requests.post(
    "https://api.ittybit.com/tasks",
    headers={"Authorization": f"Bearer {api_key}"},
    json=task,
)
data = res.json()
TASK='{
  "input": "s3://camera-raw/lobby/2024-03-15-08.mov",
  "kind": "video",
  "options": {
    "codec": "h265",
    "quality": "medium",
    "fps": 15,
    "format": "mp4"
  },
  "output": "s3://camera-archive/lobby/2024-03-15-08.mp4"
}'

curl -X POST https://api.ittybit.com/tasks \
  -H "Authorization: Bearer $ITTYBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$TASK"