On this page
Developer guide

Transcode

src/Transcode/ handles CMAF video transcoding via FFmpeg. It is the most infrastructure-heavy context in the codebase — it manages long-running encoding jobs, produces HLS v6 and DASH manifests, implements a seek-aware segment priority queue, and offloads CPU-bound FFmpeg work to the Swoole process pool.

Domain Models

Aggregate Roots

Root Description
TranscodeJob A single encoding job with status, progress tracking, and quality tier
TranscodeSession A live transcoding session with real-time state, priority, and lifecycle
SegmentMetadata Metadata for an encoded segment

Value Objects

Type Kind Description
QualityTier Value object HEVC quality definitions (resolution, bitrate, codec parameters)
AudioProfile Value object Audio encoding settings (codec, bitrate, channels)
LoudnessStandard Value object Audio normalization targets (e.g., EBU R128)
SessionState Enum Pending, Preparing, Active, Paused, Completed, Failed, Cancelled
SessionPriority Value object Session priority for the segment queue
TranscodeStatus Enum Job status values
VideoProbeResult Value object Parsed output from FFprobe

Domain Services

Service Purpose
QualityLadder Defines the available quality tiers and their encoding parameters
AudioProcessingRules Rules for audio encoding, normalization, and loudness targeting

Commands & Handlers

Command Handler Purpose
CreateTranscodeSessionCommand CreateTranscodeSessionHandler Start a new transcoding session
PauseTranscodeSessionCommand PauseTranscodeSessionHandler Pause an active session
ResumeTranscodeSessionCommand ResumeTranscodeSessionHandler Resume a paused session
CancelTranscodeSessionCommand CancelTranscodeSessionHandler Cancel a session and clean up resources
UpdateTranscodeSessionCommand UpdateTranscodeSessionHandler Update session state
UpdateTranscodePositionCommand UpdateTranscodePositionHandler Update playback position (drives the segment priority queue)
CleanupOrphanedJobsCommand CleanupOrphanedJobsHandler Remove stale or incomplete jobs

Ports

This context defines the following port interfaces, among others:

Port Purpose
FFmpegPortInterface FFmpeg process management (probe, encode, segment)
TranscodeJobPortInterface Job lifecycle operations
TranscodeSessionPortInterface Session lifecycle operations
SegmentCachePortInterface In-memory caching of encoded segments
TranscodeStoragePortInterface Persistent storage of segment files
TranscodeStreamingPortInterface Streaming segment delivery to clients
StreamAuthPortInterface Stream signing/authentication
AudioRenditionPortInterface Cached on-the-fly audio transcoding of a track to a format and bitrate; published to Media through the Transcode Audio Rendition Contract Deptrac layer with AudioRenditionFormat, AudioRenditionFailedException and TranscodeSettingDefinitions

Audio Renditions

Media's GET /api/stream/track streams a track transcoded to Opus, AAC or MP3 when the request names a format. Media checks library access and reads the system settings transcode.enabled (off by default) and transcode.max_bitrate (kbps, default 320) on every request; TranscodeSettingDefinitions contributes both. With transcoding off, a request with a format is 403 and no encode starts. Otherwise Media caps the requested bitrate at the maximum, or uses the maximum when none is requested, and calls AudioRenditionPortInterface::open(). AudioRenditionFormat::supportedBitrate() then snaps the bitrate down to the highest step of BITRATE_LADDER that the format supports (seven steps from 64 to 320 kbps; Opus at most 256 kbps), limiting each format to at most seven bitrate choices.

AudioRenditionCache admits durable work through AudioRenditionRuntimePort. The runtime key includes the track, format, bitrate and source signature. The signature fingerprints the resolved source path, modification time and size, so a repaired source rename selects a separate rendition even when the track ID and file metadata stay unchanged. The output root is audio-renditions/<track>/<signature>/ under the transcode storage root. Repeated requests for the unchanged source reuse that identity. This is a path and metadata fingerprint; it does not detect a same-path replacement that preserves both size and modification time.

The media worker discovers committed pending admissions and claims an execution generation. AudioRenditionExecutor verifies the source signature before encoding, holds the rendition's filesystem lock and delegates FFmpeg to native execution. Each generation writes its own .part file, then renames it to the final file while holding durable ownership. A busy filesystem lock defers the attempt. An expired claim can be recovered in a new generation; stale workers cannot publish its output. Encoding requires the separately deployed media worker, including for requests made outside Swoole.

A complete rendition is served as a file with Range support. Otherwise the request waits for its accepted generation to produce bytes and returns a progressive stream with Accept-Ranges: none. The reader remains pinned to that generation and rejects ownership loss. A failed encode or startup timeout raises AudioRenditionFailedException, which Media maps to HTTP 500.

SweepTranscodeCacheHandler treats audio-renditions/<track> as one cache unit under the same TTL and size budget as video directories. A rename can leave the previous completed output until that sweep removes it. If a completed file has been evicted, the next request resets the observed completed generation and waits for a worker to rebuild it from the same source.

Domain Events

Event Trigger
TranscodeJobCreated A new encoding job is created
TranscodeJobCompleted An encoding job finishes successfully
TranscodeJobFailed An encoding job fails
TranscodeSessionAttached A client attaches to a transcoding session
PlaybackPositionChanged The playback position in a session changes (drives the segment priority queue)

API Endpoints

Endpoints are split between session management and streaming delivery.

Session Management

Method Path Purpose
GET /api/transcode/sessions List transcoding sessions
POST /api/transcode/sessions Create a new session
GET /api/transcode/sessions/{uuid} Get a single session
PATCH /api/transcode/sessions/{uuid} Update a session
PATCH /api/transcode/sessions/{uuid}/pause Pause a session
PATCH /api/transcode/sessions/{uuid}/resume Resume a paused session
POST /api/transcode/sessions/{uuid}/position Update playback position
DELETE /api/transcode/sessions/{uuid} Cancel a session

Job Management

Method Path Purpose
GET /api/transcode/jobs List transcoding jobs
GET /api/transcode/jobs/{publicId} Get a single job status
GET /api/transcode/jobs/{publicId}/metrics Job encoding metrics
POST /api/transcode/jobs/cleanup Trigger orphaned-job cleanup

Streaming

Method Path Purpose
POST /api/stream/sign Sign a master manifest URL for playback (token and DPoP proof required; library membership checked)
GET /api/transcode/{videoId}/quality-ladder Available quality tiers (token and DPoP proof required; library membership checked)
GET /api/transcode/{videoId}/master.m3u8 HLS master manifest (signed URL)
GET /api/transcode/{videoId}/manifest.mpd DASH manifest (signed URL)
GET /api/transcode/{jobPublicId}/media.m3u8 HLS media playlist (signed URL)
GET /api/transcode/{jobPublicId}/subtitles/{language}/media.m3u8 HLS subtitle manifest (signed URL)
GET /api/transcode/{jobPublicId}/init Init segment (signed URL)
GET /api/transcode/{jobPublicId}/segment?index={index} Media segment (signed URL)
GET /api/transcode/{jobPublicId}/subtitles/{language}/{segment}.vtt Subtitle segment (signed URL)

Cross-Context Relationships

Direction Context Details
Depends on Shared Uuid, PublicId, ProcessPool, Async, JobMonitoringMiddleware
Depended on by Party References transcode jobs for synchronized playback
Depended on by Media The track stream transcodes audio through AudioRenditionPortInterface and reads TranscodeSettingDefinitions keys (the Transcode Audio Rendition Contract Deptrac layer)
Depended on by Notification Listens to transcode domain events for user notifications
Depends on QoL QoL contracts only. StreamAdmissionListener (on TranscodeSessionAttached, priority 1) and StreamCompletionListener (on TranscodeJobCompleted) call the stream-admission contract; QualityFilteringStreamingDecorator filters manifests by the allowed-tier contract (decoration priority -1, below the cache decorator); QualityLadderPort and EncoderProfileFingerprintAdapter implement QoL contracts

Infrastructure

FFmpeg

Component Purpose
AudioEncoder Audio-only encoding
VideoEncoder Video encoding with quality tier parameters
SegmentEncoder CMAF segment encoding
FFmpegProbeAdapter Media file probing (duration, codecs, resolution)

Manifest Generation

Component Purpose
HlsSegmentWriter Writes HLS segments to storage
HlsManifestGenerator Generates HLS v6 playlists
QualityLadderRenderer Renders quality tier information for clients
DashManifestGenerator Generates DASH manifests

Caching and Storage

Component Purpose
InMemorySegmentCache Hot cache for recently encoded segments
TranscodeFileStorage Persistent file storage for segments and manifests
SegmentFileResolver Resolves segment paths between cache and storage

Swoole Integration

Component Purpose
ProcessPool integration CPU-bound FFmpeg workers via Unix sockets
Graceful restart handling Ensures in-flight transcodes survive worker restarts
Job persistence Jobs survive process restarts by persisting state to the database