Skip to content

ATEM ISO EDL Generator

Frame-accurate Edit Decision List generation from Blackmagic ATEM switchers

Licence: MIT TypeScript Node.js


Overview

ATEM ISO EDL Generator captures switching events from Blackmagic ATEM switchers in real-time and generates industry-standard EDL files for post-production. Unlike ATEM Mini ISO’s proprietary DaVinci Resolve project files, this tool produces universal CMX 3600 EDLs compatible with any NLE.

Key Features

  • Real-time event capture — Monitors ATEM programme bus changes via network
  • Frame-accurate timestamps — Sub-frame precision with configurable timecode sources
  • HyperDeck integration — Reads RP-188 embedded timecode from SDI, queries clip metadata
  • Universal EDL output — CMX 3600 format with extended comments for file matching
  • Web-based monitoring — Live dashboard with source configuration and export controls
  • Pluggable timecode — System clock or HyperDeck SDI timecode with automatic fallback

Use Cases

  • Live multicam productions — Generate EDLs for ISO recordings during transmission
  • Broadcast workflows — Integrate with HyperDeck-based ingest infrastructure
  • Post-production handoff — Deliver frame-accurate cut lists to offline editors
  • Archive documentation — Maintain searchable logs of all switching decisions

Architecture

┌─────────────────┐ ┌──────────────────────────────────┐ ┌─────────────────┐
│ ATEM Switcher │────▶│ ATEM ISO EDL Generator │────▶│ EDL Files │
│ (UDP 9910) │ │ │ │ (.edl) │
└─────────────────┘ │ ┌────────────────────────────┐ │ └─────────────────┘
│ │ ATEM Adapter │ │
┌─────────────────┐ │ │ • Programme bus monitoring │ │ ┌─────────────────┐
│ HyperDeck(s) │────▶│ │ • Transition detection │ │────▶│ Event Log │
│ (TCP 9993) │ │ │ • M/E bank selection │ │ │ (.jsonl) │
└─────────────────┘ │ └────────────────────────────┘ │ └─────────────────┘
│ │
┌─────────────────┐ │ ┌────────────────────────────┐ │
│ Web Browser │◀───▶│ │ Timecode Provider │ │
│ │ │ │ • System clock source │ │
└─────────────────┘ │ │ • HyperDeck RP-188 source │ │
│ │ • Automatic fallback │ │
│ └────────────────────────────┘ │
│ │
│ ┌────────────────────────────┐ │
│ │ Web Interface │ │
│ │ • Real-time dashboard │ │
│ │ • Source configuration │ │
│ │ • EDL export controls │ │
│ └────────────────────────────┘ │
└──────────────────────────────────┘

Components

ComponentPurposeTechnology
ATEM AdapterConnects to ATEM switcher, captures programme changesatem-connection library
HyperDeck AdapterMonitors recording status, reads clip metadataBlackmagic Ethernet Protocol
Timecode ProviderManages timecode acquisition with fallback chainSystem clock / RP-188 SDI
Event StorePersists all events with timestampsJSONL append-only log
EDL GeneratorConverts events to CMX 3600 formatCustom TypeScript
Web ServerDashboard, configuration UI, WebSocket updatesExpress + vanilla JS

Installation

Prerequisites

  • Node.js 20+ (LTS recommended)
  • Network access to ATEM switcher (UDP port 9910)
  • Network access to HyperDeck(s) (TCP port 9993)

Quick Start

Terminal window
# Clone the repository
git clone https://github.com/FiLORUX/atem-iso-edl-generator.git
cd atem-iso-edl-generator
# Install dependencies
npm install
# Copy example configuration
cp config/config.example.yaml config/config.yaml
# Edit configuration with your device IPs
nano config/config.yaml
# Start the service
npm start

Docker Deployment

Terminal window
# Build the image
docker build -t atem-edl-generator .
# Run with host networking for device discovery
docker run -d \
--name atem-edl \
--network host \
-v $(pwd)/config:/app/config \
-v $(pwd)/output:/app/output \
atem-edl-generator

Configuration

Configuration is managed via YAML files in the config/ directory.

Basic Configuration

config/config.yaml
# ATEM Switcher Connection
atem:
host: "10.7.77.7"
mixEffect: 0 # M/E bank to monitor (0 = M/E 1)
frameOffset: 1 # Compensate for processing latency
# Input Sources
inputs:
1:
name: "Cam 1 - Centre Wide"
reelName: "CAM1" # 8 chars max for EDL compatibility
filePrefix: "CAM1_" # Expected prefix in HyperDeck recordings
2:
name: "Cam 2 - Centre MCU"
reelName: "CAM2"
filePrefix: "CAM2_"
3:
name: "Cam 3 - Centre ECU"
reelName: "CAM3"
filePrefix: "CAM3_"
4:
name: "Cam 4 - Steadicam"
reelName: "CAM4"
filePrefix: "CAM4_"
# HyperDeck ISO Recorders
hyperdecks:
- name: "ISO-1"
host: "10.7.77.21"
inputMapping: 1 # Records ATEM input 1
- name: "ISO-2"
host: "10.7.77.22"
inputMapping: 2
- name: "ISO-3"
host: "10.7.77.23"
inputMapping: 3
- name: "ISO-4"
host: "10.7.77.24"
inputMapping: 4
# EDL Output Settings
edl:
outputDirectory: "./output"
format: "cmx3600"
frameRate: 25 # 23.976, 24, 25, 29.97, 30, 50, 59.94, 60
dropFrame: false # true for 29.97/59.94 drop-frame
includeComments: true # Add FROM CLIP NAME and SOURCE FILE comments
# Timecode Settings
timecode:
source: "system" # "system" or "hyperdeck"
frameRate: 25
startTimecode: "01:00:00:00"
# HyperDeck timecode source (when source: "hyperdeck")
hyperdeck:
host: "10.7.77.21"
port: 9993
requireSdiSource: true # Only use RP-188 embedded timecode
# Fallback to system clock if primary fails
fallback:
enabled: true
delayMs: 3000
# Web Interface
web:
enabled: true
port: 3000
host: "0.0.0.0"

Usage

Starting a Recording Session

  1. Start the servicenpm start or via Docker
  2. Open the web UI — Navigate to http://localhost:3000
  3. Verify connections — Check that ATEM shows “Connected” on the dashboard
  4. Start HyperDeck recording — Begin ISO recording on all decks
  5. Run your show — All programme changes are logged automatically
  6. Export — Click “Download EDL” in the Export tab

Web Interface

The web interface provides three main views:

TabPurpose
DashboardLive programme/preview display, connection status, event log
ExportEDL format selection, download controls, export history
SettingsSource configuration, ATEM connection, timecode settings

Command Line

Terminal window
# Start with default config
npm start
# Development mode with hot reload
npm run dev
# Build for production
npm run build

EDL Output Format

Generated EDLs follow the CMX 3600 specification with extended comment lines for modern NLE compatibility.

Sample Output

TITLE: LIVE_PRODUCTION_2026-01-28
001 CAM1 V C 01:00:00:00 01:00:12:18 01:00:00:00 01:00:12:18
* FROM CLIP NAME: CAM1_A001_20260128.MOV
* SOURCE FILE: /Volumes/ISO/CAM1_A001_20260128.MOV
* ATEM INPUT: 1 (Cam 1 - Centre Wide)
002 CAM2 V C 01:00:10:05 01:00:25:12 01:00:12:18 01:00:28:00
* FROM CLIP NAME: CAM2_A001_20260128.MOV
* SOURCE FILE: /Volumes/ISO/CAM2_A001_20260128.MOV
* ATEM INPUT: 2 (Cam 2 - Centre MCU)
003 CAM1 V D 025 01:00:23:00 01:00:45:10 01:00:28:00 01:00:50:10
* FROM CLIP NAME: CAM1_A001_20260128.MOV
* ATEM INPUT: 1 (Cam 1 - Centre Wide)
* TRANSITION: MIX 25 frames

EDL Field Reference

FieldDescription
Event NumberSequential edit number (001-999)
Reel Name8-character source identifier from config
TrackV (video), A (audio), AA (stereo)
TransitionC (cut), D (dissolve/mix), W (wipe)
Source In/OutTimecode in source recording
Record In/OutTimecode on master timeline

API Reference

REST Endpoints

EndpointMethodDescription
/api/statusGETConnection status and session info
/api/eventsGETList captured events (paginated)
/api/configGETCurrent configuration
/api/configPOSTUpdate configuration
/api/edl/generatePOSTGenerate EDL from current session
/api/healthGETHealth check endpoint

WebSocket

Connect to ws://localhost:3000/ws for real-time updates:

// Message types received
{
"type": "initial_state", // Full state on connect
"type": "program_change", // Programme bus changed
"type": "connection_status", // Device connected/disconnected
"type": "event" // New event captured
}

Troubleshooting

Common Issues

ProblemCauseSolution
ATEM connection failsFirewall blocking UDP 9910Allow UDP 9910 bidirectional
HyperDeck disconnectedWrong IP or deck powered offVerify IP and power status
Timecode jumpingSystem clock adjustmentUse HyperDeck RP-188 source
Missing eventsInput not in mappingAdd input to configuration
EDL won’t importFrame rate mismatchMatch EDL frame rate to project

Debug Mode

Terminal window
# Enable verbose logging
npm run dev
# Or set log level in config
logging:
level: "debug"

Development

Project Structure

atem-iso-edl-generator/
├── src/
│ ├── adapters/ # Device communication
│ │ ├── atem/ # ATEM switcher protocol
│ │ └── hyperdeck/ # HyperDeck Ethernet Protocol
│ ├── core/ # Core business logic
│ │ ├── config/ # Configuration schema and loading
│ │ └── events/ # Event types and persistence
│ ├── providers/ # Pluggable subsystems
│ │ └── timecode/ # Timecode acquisition chain
│ ├── generators/ # Output format generators
│ │ └── edl/ # CMX 3600 EDL generation
│ ├── web/ # Web interface
│ │ ├── api/ # REST API routes
│ │ ├── ws/ # WebSocket handlers
│ │ └── static/ # Frontend assets
│ └── app.ts # Application entry point
├── config/ # Configuration files
├── output/ # Generated EDL files
└── logs/ # Event logs

Running Tests

Terminal window
npm test # Run all tests
npm run test:coverage # With coverage report
npm run test:watch # Watch mode

Licence

MIT Licence — see LICENSE for details.


Acknowledgements


Built for broadcast. Designed for reliability.


David Thåst · thåst.se · FiLORUX