Skip to main content

Overview

MCSP processes video and audio content through a shared, fully asynchronous ingestion pipeline. Both content types follow the same event-driven workflow from upload through to publication. The pipeline branches at the transcoding stage — video jobs use GPU-accelerated workers for high resolutions; audio jobs use CPU-only workers and complete significantly faster on average. The pipeline is designed for progressive availability: the lowest-quality variant of a content item is published as soon as it is ready (typically 2 minutes for video 360p, 30 seconds for audio 64 kbps) while higher-quality variants continue transcoding in parallel.
Architecture note: The pipeline is stateless at each stage. A stage consumes an upstream Kafka event, performs its work, and emits a downstream event. Stages fail independently and retry without restarting the entire pipeline. See ADR-001 for the rationale for this design.

Pipeline Stages

StageServiceVideo OutputAudio Output
0. Session & metadataUpload ServiceSession created; presigned URL issued; metadata saved as draftSame
1. Multipart uploadClient → Object StorageRaw file in staging bucketRaw file in staging bucket
2. Format & size validationUpload IngestorReject if unsupported format or > 20 GBReject if unsupported format or > 2 GB
3. Virus scanClamAV / cloud malware scannerCLEAN or QUARANTINECLEAN or QUARANTINE
4. Copyright fingerprint scanAI Copyright ScannerVideo perceptual hash + audio fingerprintAudio fingerprint (Audible Magic / in-house)
5. Transcoding orchestrationKafka consumer → job dispatcherOne job per target resolutionOne job per target bitrate
6. TranscodingFFmpeg workersMP4 encoded segments per resolution (GPU for 4K)AAC-LC segments per bitrate (CPU-only)
7. HLS/DASH packagingShaka PackagerfMP4 + .m3u8 + .mpd (video + audio tracks)fMP4 + audio-only .m3u8 + audio .mpd
8. DRM encryptionShaka + CEK from KMSEncrypted video+audio segments, PSSH boxesEncrypted audio segments, PSSH boxes
9. Art generationArt Generator3–5 thumbnail candidates (FFmpeg frame extract)Cover art from creator-uploaded image
10. Storage routingResidency Policy EngineWrite to global hot bucket or Nigeria residency bucketSame
11. Metadata indexingElasticsearch IndexerDocument with content_type: VIDEODocument with content_type: AUDIO
12. PublishContent ServiceStatus → PUBLISHED; media.published emittedSame

Step-by-Step Flow

1

Upload session creation

Creator calls POST /api/v1/uploads/session. The Upload Service validates the JWT, queries the Residency Policy Engine, creates a session in Redis, persists metadata to Postgres, and returns { session_id, presigned_url, content_id }.
2

Direct multipart upload

The creator uploads the file directly to object storage via the presigned URL. The Upload Service is not in the data path. Resumable uploads are supported via multipart part tracking in the session state.
3

Pipeline initiation

Object storage emits an S3 completion event. The Upload Service marks the session complete and emits media.upload.initiated to Kafka.
4

Format validation and virus scan

The Upload Ingestor validates format and size limits (video ≤ 20 GB, audio ≤ 2 GB), runs the virus scan, and emits media.upload.completed. Quarantined files emit media.upload.blocked and are soft-deleted.
5

Copyright fingerprint scan

The Copyright Scanner runs AI fingerprint matching (video perceptual hash + audio fingerprint against the known infringement database). Cleared content emits media.copyright.cleared. Matched content emits media.upload.blocked with the match reason.
6

Transcoding

The Transcoding Orchestrator receives media.copyright.cleared and fans out one job per resolution (video) or bitrate variant (audio). Workers pull jobs from the queue and emit media.transcoding.completed per completed variant. Lower-quality variants are published first (progressive availability).
7

DRM packaging

The DRM Packager receives media.transcoding.completed, retrieves the CEK from KMS, encrypts the segments using CMAF CENC/CBCS, injects PSSH boxes, writes the encrypted output to the appropriate hot or residency bucket, and emits media.drm.packaged.
8

Art generation and indexing

In parallel with DRM packaging: the Art Generator produces thumbnails (video: frame extract) or processes cover art (audio). The Metadata Indexer writes the content document to Elasticsearch.
9

Publication

The Content Service receives media.drm.packaged, updates the content status to PUBLISHED, and emits media.published. The Notification Service, ML Feature Store, and Search Indexer fan-out from this event.
Residency decisions are immutable. Once the Residency Policy Engine records a NIGERIA or GLOBAL decision at upload time, that decision cannot be changed by any application principal. Enforcement is backed by cloud IAM bucket policy — not application code.

Kafka Topics Used

TopicStageDirection
media.upload.initiatedPipeline initiationProduced by Upload Service
media.upload.completedPost-validationProduced by Upload Ingestor
media.copyright.clearedPost-copyright scanProduced by Copyright Scanner
media.transcoding.completedPer variant transcodedProduced by Transcoding Cluster
media.publishedPublicationProduced by Content Service

Video Transcoding Targets

ResolutionBitrate TargetPriority
360p400 kbps video + 64 kbps audioFirst — available within ~2 minutes
480p800 kbps video + 128 kbps audioSecond
720p2.5 Mbps video + 128 kbps audioThird — most common playback tier
1080p5 Mbps video + 192 kbps audioFourth — HD tier
4K (2160p)16 Mbps video + 320 kbps audioLast — GPU worker, qualifying content only

Audio Transcoding Targets

BitrateTargetPriority
64 kbps AAC-LCLow bandwidth / mobileFirst — available within ~30 seconds
128 kbps AAC-LCStandard qualityDefault playback tier
256 kbps AAC-LCHigh qualityAd-free subscribers
320 kbps AAC-LCLossless-equivalentPremium tier; qualifying content only

Failure Handling

Idempotency is mandatory for all async workers. Before executing any operation, every Kafka consumer checks a Redis-backed idempotency store keyed on {topic}:{partition}:{offset}. Processed offsets expire after 24 hours. Billing operations additionally pass processor-issued idempotency keys to Paystack/Stripe.
FailureBehaviour
Virus scan positiveFile quarantined. media.upload.blocked emitted. Creator notified. No pipeline continuation.
Copyright matchmedia.upload.blocked emitted. Pipeline stopped. Creator notified with category.
Transcoding job failureRetried with exponential backoff (max 5 attempts). After 5 failures, moves to dead-letter queue. Creator notified.
Partial transcode failureIf 4K fails but 1080p succeeds, lower resolutions are published immediately. The failed variant is retried independently.
DRM packaging failureJob retried. If packaging fails for a variant, that variant is not published. Lower variants that have already been packaged remain available.
Spot instance interruptionWorker checkpoint system limits lost progress to ≤ 30 seconds. Job resumes from last checkpoint, not from the beginning.

Build docs developers (and LLMs) love