Security Camera Video Storage

Store and manage surveillance video footage from security cameras

All recipes· security-surveillance· 12 minutesintermediate

Security Camera Video Storage

Objective

Create a system for storing and managing surveillance video footage from security cameras with support for multiple locations, retention policies, and efficient retrieval.

Step 1: Create Camera Locations Table

Define camera installation sites.

CREATE TABLE camera_locations (
    id INTEGER PRIMARY KEY,
    location_code VARCHAR(50) NOT NULL UNIQUE,
    location_name VARCHAR(200),
    building VARCHAR(100),
    floor VARCHAR(20),
    zone VARCHAR(100),
    area_type VARCHAR(50),
    gps_latitude DECIMAL(10, 8),
    gps_longitude DECIMAL(11, 8),
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Step 2: Create Security Cameras Table

Store camera details.

CREATE TABLE security_cameras (
    id INTEGER PRIMARY KEY,
    camera_id VARCHAR(50) NOT NULL UNIQUE,
    camera_name VARCHAR(200),
    location_id INTEGER NOT NULL,
    camera_type VARCHAR(50),
    manufacturer VARCHAR(100),
    model VARCHAR(100),
    resolution VARCHAR(20),
    frame_rate INTEGER DEFAULT 30,
    has_audio BOOLEAN DEFAULT FALSE,
    has_ptz BOOLEAN DEFAULT FALSE,
    has_night_vision BOOLEAN DEFAULT TRUE,
    ip_address VARCHAR(50),
    status VARCHAR(50) DEFAULT 'online',
    install_date DATE,
    last_maintenance DATE,
    FOREIGN KEY (location_id) REFERENCES camera_locations(id)
);

Step 3: Create Video Recordings Table

Store recorded footage.

CREATE TABLE video_recordings (
    id INTEGER PRIMARY KEY,
    camera_id INTEGER NOT NULL,
    recording_uid VARCHAR(100) UNIQUE,
    video_file VIDEO(MP4),
    thumbnail IMAGE(JPEG),
    start_time TIMESTAMP NOT NULL,
    end_time TIMESTAMP NOT NULL,
    duration_seconds INTEGER,
    file_size_mb DECIMAL(10, 2),
    resolution VARCHAR(20),
    frame_rate INTEGER,
    codec VARCHAR(50),
    has_motion BOOLEAN DEFAULT FALSE,
    motion_events INTEGER DEFAULT 0,
    quality_score DECIMAL(3, 2),
    is_archived BOOLEAN DEFAULT FALSE,
    retention_days INTEGER DEFAULT 30,
    expires_at TIMESTAMP,
    FOREIGN KEY (camera_id) REFERENCES security_cameras(id)
);

Step 4: Create Video Segments Table

Store chunked recordings for streaming.

CREATE TABLE video_segments (
    id INTEGER PRIMARY KEY,
    recording_id INTEGER NOT NULL,
    segment_number INTEGER,
    segment_file VIDEO(MP4),
    start_offset_sec INTEGER,
    duration_sec INTEGER,
    file_size_mb DECIMAL(8, 2),
    is_keyframe BOOLEAN DEFAULT FALSE,
    FOREIGN KEY (recording_id) REFERENCES video_recordings(id)
);

Step 5: Create Recording Tags Table

Label recordings with metadata.

CREATE TABLE recording_tags (
    id INTEGER PRIMARY KEY,
    recording_id INTEGER NOT NULL,
    tag_name VARCHAR(100),
    tag_value VARCHAR(255),
    tagged_by VARCHAR(100),
    tagged_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (recording_id) REFERENCES video_recordings(id)
);

Step 6: Insert Camera Locations

Add surveillance zones.

INSERT INTO camera_locations (id, location_code, location_name, building, floor, zone, area_type, gps_latitude, gps_longitude) VALUES
    (1, 'LOC-ENT-MAIN', 'Main Entrance', 'Building A', 'Ground', 'Entrance Zone', 'entrance', 37.78825000, -122.40730000),
    (2, 'LOC-LOBBY-A', 'Main Lobby', 'Building A', 'Ground', 'Lobby Zone', 'lobby', 37.78830000, -122.40735000),
    (3, 'LOC-PARK-A', 'Parking Garage Level 1', 'Parking A', 'P1', 'Parking Zone', 'parking', 37.78815000, -122.40745000),
    (4, 'LOC-PARK-B', 'Parking Garage Level 2', 'Parking A', 'P2', 'Parking Zone', 'parking', 37.78815000, -122.40745000),
    (5, 'LOC-SERV-A', 'Server Room', 'Building A', '2', 'Secure Zone', 'restricted', 37.78840000, -122.40720000),
    (6, 'LOC-ELEV-A1', 'Elevator Bank A', 'Building A', 'Ground', 'Common Area', 'elevator', 37.78832000, -122.40732000),
    (7, 'LOC-STAIR-A1', 'Stairwell A North', 'Building A', 'All', 'Emergency Exit', 'stairwell', 37.78835000, -122.40728000),
    (8, 'LOC-LOAD-A', 'Loading Dock', 'Building A', 'Ground', 'Service Zone', 'loading', 37.78820000, -122.40750000);

Step 7: Insert Security Cameras

Add camera inventory.

INSERT INTO security_cameras (id, camera_id, camera_name, location_id, camera_type, manufacturer, model, resolution, frame_rate, has_audio, has_ptz, has_night_vision, ip_address, status, install_date) VALUES
    (1, 'CAM-ENT-001', 'Main Entrance Camera 1', 1, 'dome', 'Axis', 'P3245-V', '1080p', 30, TRUE, TRUE, TRUE, '192.168.1.101', 'online', '2023-01-15'),
    (2, 'CAM-ENT-002', 'Main Entrance Camera 2', 1, 'bullet', 'Hikvision', 'DS-2CD2085', '4K', 25, FALSE, FALSE, TRUE, '192.168.1.102', 'online', '2023-01-15'),
    (3, 'CAM-LOBBY-001', 'Lobby Overview', 2, 'dome', 'Axis', 'P3245-V', '1080p', 30, TRUE, TRUE, TRUE, '192.168.1.103', 'online', '2023-01-15'),
    (4, 'CAM-PARK-P1-001', 'Parking P1 Entrance', 3, 'bullet', 'Hikvision', 'DS-2CD2085', '4K', 25, FALSE, FALSE, TRUE, '192.168.1.104', 'online', '2023-02-01'),
    (5, 'CAM-PARK-P1-002', 'Parking P1 Interior', 3, 'dome', 'Axis', 'M3057-PLVE', '1080p', 15, FALSE, FALSE, TRUE, '192.168.1.105', 'online', '2023-02-01'),
    (6, 'CAM-SERVER-001', 'Server Room Entry', 5, 'dome', 'Axis', 'P3245-V', '4K', 30, TRUE, FALSE, TRUE, '192.168.1.106', 'online', '2023-01-20'),
    (7, 'CAM-SERVER-002', 'Server Room Interior', 5, 'fisheye', 'Axis', 'M3057-PLVE', '1080p', 15, TRUE, FALSE, TRUE, '192.168.1.107', 'online', '2023-01-20'),
    (8, 'CAM-LOAD-001', 'Loading Dock', 8, 'bullet', 'Hikvision', 'DS-2CD2685', '4K', 25, TRUE, TRUE, TRUE, '192.168.1.108', 'online', '2023-03-01');

Step 8: Insert Video Recordings

Add sample recordings.

INSERT INTO video_recordings (id, camera_id, recording_uid, start_time, end_time, duration_seconds, file_size_mb, resolution, frame_rate, codec, has_motion, motion_events, quality_score, retention_days, expires_at) VALUES
    (1, 1, 'REC-20240115-CAM001-0800', '2024-01-15 08:00:00', '2024-01-15 09:00:00', 3600, 1250.50, '1080p', 30, 'H.264', TRUE, 45, 0.95, 30, '2024-02-14 09:00:00'),
    (2, 1, 'REC-20240115-CAM001-0900', '2024-01-15 09:00:00', '2024-01-15 10:00:00', 3600, 1320.75, '1080p', 30, 'H.264', TRUE, 62, 0.92, 30, '2024-02-14 10:00:00'),
    (3, 2, 'REC-20240115-CAM002-0800', '2024-01-15 08:00:00', '2024-01-15 09:00:00', 3600, 2450.00, '4K', 25, 'H.265', TRUE, 38, 0.98, 30, '2024-02-14 09:00:00'),
    (4, 4, 'REC-20240115-CAM004-0700', '2024-01-15 07:00:00', '2024-01-15 08:00:00', 3600, 2180.25, '4K', 25, 'H.265', TRUE, 28, 0.94, 30, '2024-02-14 08:00:00'),
    (5, 6, 'REC-20240115-CAM006-0000', '2024-01-15 00:00:00', '2024-01-15 01:00:00', 3600, 2890.00, '4K', 30, 'H.265', FALSE, 2, 0.97, 90, '2024-04-15 01:00:00'),
    (6, 6, 'REC-20240115-CAM006-0800', '2024-01-15 08:00:00', '2024-01-15 09:00:00', 3600, 2950.50, '4K', 30, 'H.265', TRUE, 15, 0.96, 90, '2024-04-15 09:00:00'),
    (7, 8, 'REC-20240115-CAM008-0600', '2024-01-15 06:00:00', '2024-01-15 07:00:00', 3600, 2100.00, '4K', 25, 'H.265', TRUE, 8, 0.93, 30, '2024-02-14 07:00:00'),
    (8, 3, 'REC-20240115-CAM003-1200', '2024-01-15 12:00:00', '2024-01-15 13:00:00', 3600, 1450.25, '1080p', 30, 'H.264', TRUE, 85, 0.91, 30, '2024-02-14 13:00:00');

Step 9: Insert Video Segments

Add streaming segments.

INSERT INTO video_segments (id, recording_id, segment_number, start_offset_sec, duration_sec, file_size_mb, is_keyframe) VALUES
    (1, 1, 1, 0, 300, 104.20, TRUE),
    (2, 1, 2, 300, 300, 108.50, FALSE),
    (3, 1, 3, 600, 300, 102.80, TRUE),
    (4, 1, 4, 900, 300, 105.30, FALSE),
    (5, 1, 5, 1200, 300, 110.20, TRUE),
    (6, 1, 6, 1500, 300, 106.40, FALSE),
    (7, 2, 1, 0, 300, 110.50, TRUE),
    (8, 2, 2, 300, 300, 112.30, FALSE),
    (9, 2, 3, 600, 300, 108.90, TRUE);

Step 10: Insert Recording Tags

Add metadata tags.

INSERT INTO recording_tags (id, recording_id, tag_name, tag_value, tagged_by) VALUES
    (1, 1, 'event_type', 'high_traffic', 'system'),
    (2, 1, 'time_of_day', 'morning_rush', 'system'),
    (3, 2, 'incident', 'suspicious_activity', 'security_operator'),
    (4, 2, 'reviewed', 'true', 'J. Smith'),
    (5, 5, 'area', 'restricted_access', 'system'),
    (6, 6, 'maintenance', 'technician_visit', 'security_operator'),
    (7, 7, 'delivery', 'scheduled', 'system'),
    (8, 8, 'crowd', 'lunch_rush', 'system');

Step 11: Get Camera Recordings

List recordings for a camera.

SELECT
    vr.recording_uid,
    vr.start_time,
    vr.end_time,
    vr.duration_seconds / 60 as duration_min,
    vr.file_size_mb,
    vr.resolution,
    vr.has_motion,
    vr.motion_events,
    vr.quality_score
FROM video_recordings vr
WHERE vr.camera_id = 1
ORDER BY vr.start_time DESC
LIMIT 10;

Step 12: Get Recordings by Location

Find footage from a zone.

SELECT
    cl.location_name,
    sc.camera_name,
    vr.start_time,
    vr.duration_seconds / 60 as duration_min,
    vr.motion_events,
    vr.file_size_mb
FROM video_recordings vr
INNER JOIN security_cameras sc ON vr.camera_id = sc.id
INNER JOIN camera_locations cl ON sc.location_id = cl.id
WHERE cl.area_type = 'entrance'
  AND vr.start_time >= '2024-01-15 08:00:00'
  AND vr.start_time < '2024-01-15 12:00:00'
ORDER BY vr.start_time;

Step 13: Storage Usage Report

Analyze video storage.

SELECT
    cl.area_type,
    COUNT(DISTINCT sc.id) as cameras,
    COUNT(vr.id) as recordings,
    SUM(vr.file_size_mb) / 1024 as total_gb,
    AVG(vr.quality_score) as avg_quality
FROM camera_locations cl
INNER JOIN security_cameras sc ON cl.id = sc.location_id
LEFT JOIN video_recordings vr ON sc.id = vr.camera_id
GROUP BY cl.area_type
ORDER BY total_gb DESC;

Step 14: Expiring Recordings

Find recordings nearing retention limit.

SELECT
    sc.camera_name,
    vr.recording_uid,
    vr.start_time,
    vr.retention_days,
    vr.expires_at,
    vr.file_size_mb
FROM video_recordings vr
INNER JOIN security_cameras sc ON vr.camera_id = sc.id
WHERE vr.expires_at <= CURRENT_TIMESTAMP + INTERVAL '7 days'
  AND vr.is_archived = FALSE
ORDER BY vr.expires_at;

Step 15: Camera Status Overview

Monitor camera health.

SELECT
    cl.building,
    cl.zone,
    sc.camera_name,
    sc.status,
    sc.resolution,
    COUNT(vr.id) as recent_recordings,
    MAX(vr.end_time) as last_recording
FROM security_cameras sc
INNER JOIN camera_locations cl ON sc.location_id = cl.id
LEFT JOIN video_recordings vr ON sc.id = vr.camera_id
    AND vr.start_time >= CURRENT_DATE - 1
GROUP BY sc.id, cl.building, cl.zone, sc.camera_name, sc.status, sc.resolution
ORDER BY cl.building, cl.zone;

Cleanup (Optional)

DROP TABLE IF EXISTS recording_tags;
DROP TABLE IF EXISTS video_segments;
DROP TABLE IF EXISTS video_recordings;
DROP TABLE IF EXISTS security_cameras;
DROP TABLE IF EXISTS camera_locations;

Expected Outcomes

  • Cameras organized by location
  • Recordings stored with metadata
  • Segments available for streaming
  • Tags for quick retrieval
  • Storage tracked

Camera Types

Type Use Case
dome Indoor ceiling mount
bullet Outdoor long-range
fisheye 360-degree coverage
ptz Pan-tilt-zoom active

Key Concepts Learned

  • Surveillance video organization
  • Multi-camera management
  • Video segmentation
  • Retention policies
  • Storage monitoring

Tags

sqlintermediatevideosecuritysurveillancecameras

Run this on your own machine

Install SynapCores Community Edition free, paste the SQL or Cypher above into the bundled web UI, and watch it run.

Download Free CE