Archive security camera footage

View Markdown

“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
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/jobs', {
  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/jobs",
    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/jobs \
  -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
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/jobs', {
  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/jobs",
    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/jobs \
  -H "Authorization: Bearer $ITTYBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$TASK"