D
DreamLake

Lambda

Lambda Design

BSS delegates heavy media processing to AWS Lambda. A single Lambda function (hls-splitter) handles both video and audio, dispatched by the type field in the event payload.

Architecture

BSS Server

 ├─ POST /lambdas/hls-split { videoId }
 │   → Lambda invoke (type: "video")

 └─ POST /lambdas/audio-process { audioId }
     → Lambda invoke (type: "audio")
 
Lambda (hls-splitter)

 ├─ Download raw file from S3 staging pool
 ├─ Process with ffmpeg / ffprobe
 ├─ Upload results back to S3
 └─ Optional callback PATCH to BSS

Event Payload

interface MediaEvent {
  type?: 'video' | 'audio'    // default: 'video'
  videoId?: string             // required for video
  audioId?: string             // required for audio
  owner: string                // S3 namespace (owner slug)
  project: string              // S3 project slug
  stagingHash?: string         // SHA256[:16] of staged file
  rawHash?: string             // deprecated, fallback for stagingHash
  fps?: number                 // hint fps for video (overridden by ffprobe)
  callbackUrl?: string         // optional PATCH endpoint on completion
}

Video Pipeline

9-step pipeline that splits a raw video into HLS segments:

StepActionS3 ReadsS3 Writes
1Download raw file from {owner}/{project}/staging/{hash}1
2Probe actual fps with ffprobe
3Split with ffmpeg into TS segments (no re-encode, copy codec)
4Parse ffmpeg m3u8 output for segment durations
5Hash each segment, upload to global dedup pool chunks/{hash}.tsN (dedup check)0–N
6Build stream m3u8 playlist (bare hex hashes for BSS rewrite)
6.5Generate thumbnail + sprite sheet (non-fatal)0–2
7Upload stream playlist to videos/{id}/stream/{hash}.m3u81
8Update videos/{id}/meta.json with length, fps, duration, streams, thumbnail/sprite11
9Optional callback PATCH to BSS

Chunk Deduplication

Each TS segment is content-addressed by SHA256[:16]. Before uploading, the Lambda checks if chunks/{hash}.ts already exists. Duplicate segments (common in re-uploads or similar content) are skipped.

M3U8 Format

The stream playlist uses bare hex hashes as chunk references:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:6.000,
a1b2c3d4e5f67890
#EXTINF:4.500,
1234567890abcdef
#EXT-X-ENDLIST

BSS's rewriteM3u8() replaces each 16-char hex hash with a full CDN/presigned URL before sending to the client.

Thumbnail & Sprite Sheet

Generated alongside HLS segments (non-fatal — skipped on error):

AssetSizeLocation
Thumbnail640px wide, single frame at ~10% of durationvideos/{id}/thumb.jpg
Sprite160x90 tiles, 1 frame every 5s, max 10 columnsvideos/{id}/sprite.jpg

Both URLs are stored in meta.json and served via CDN.

Video Result

{
  videoId: string
  streamHash: string      // hash of the m3u8 playlist
  chunks: number          // number of TS segments
  durationSec: number
  totalFrames: number
}

Audio Pipeline

Simpler than video — probe metadata only, no splitting:

StepAction
1Download raw file from {owner}/{project}/staging/{hash}
2Probe with ffprobe: sampleRate, channels, codec, bitRate, duration
3Optional callback PATCH to BSS with metadata

Audio Result

{
  audioId: string
  durationSec: number
  sampleRate: number      // Hz (e.g. 44100, 48000)
  channels: number        // 1 = mono, 2 = stereo
  codec: string           // e.g. "aac", "opus", "pcm_s16le"
  bitRate: number
}

Configuration

Env VarDefaultDescription
S3_BUCKET(required)S3 bucket name
AWS_REGIONus-east-1AWS region
FFMPEG_PATH/opt/bin/ffmpegPath to ffmpeg binary
FFPROBE_PATH/opt/bin/ffprobePath to ffprobe binary
SEGMENT_DURATION6HLS segment duration in seconds
CDN_BASES3 direct URLCDN base URL for thumbnail/sprite

Error Handling

  • ffprobe failure: falls back to hint fps (video) or empty metadata (audio)
  • Thumbnail/sprite failure: non-fatal, URLs set to null in meta.json
  • Callback failure: non-fatal, logged as warning
  • Segment mismatch: throws error (ffmpeg output corrupted)

S3 Layout After Processing

{owner}/{project}/
  staging/{hash}                         # original upload (retained)
  videos/{videoId}/
    meta.json                            # updated with length, fps, streams, etc.
    stream/{streamHash}.m3u8             # HLS playlist
    thumb.jpg                            # thumbnail (optional)
    sprite.jpg                           # sprite sheet (optional)
chunks/
  {hash}.ts                              # global dedup pool