D
DreamLake

Key Design

Audio Splitting

Audio files are split into HLS segments via the same Lambda as video, enabling streaming playback with seek support.

Pipeline

Raw audio in staging

 ├─ 1. Download from S3: {owner}/{project}/staging/{hash}
 ├─ 2. Probe with ffprobe (sampleRate, channels, codec, duration)
 ├─ 3. Transcode + split with ffmpeg
-c:a aac -b:a 128k -vn -f hls -hls_time 6
 ├─ 4. Parse segment durations from ffmpeg m3u8
 ├─ 5. Hash each .ts segment (SHA256[:16])
 │     Upload to chunks/{hash}.ts (skip if exists = dedup)
 ├─ 6. Build stream m3u8 with bare hex hashes
 ├─ 7. Upload playlist: tracks/audio/{id}/stream/{streamHash}.m3u8
 ├─ 8. Update tracks/audio/{id}/meta.json
 └─ 9. Callback PATCH to BSS

ffmpeg Command

ffmpeg -i input.wav \
  -c:a aac -b:a 128k \            # always transcode to AAC
  -vn \                            # strip any video stream
  -f hls \
  -hls_time 6 \                    # 6-second segments
  -hls_list_size 0 \               # keep all segments
  -hls_segment_type mpegts \
  -hls_flags independent_segments \
  -hls_segment_filename seg_%05d.ts \
  out.m3u8

Key difference from video: audio always transcodes to AAC regardless of input codec (WAV, FLAC, MP3, etc.). This ensures universal HLS compatibility. The original raw file is preserved in the staging pool.

Why Always Transcode?

Input CodecHLS Compatible?Decision
AACYesTranscode anyway — consistent output
MP3PartiallyTranscode for reliability
WAV/PCMNoMust transcode
FLACNoMust transcode
OpusNo (in TS)Must transcode

Transcoding to AAC 128k is fast and produces consistent, small segments. Quality loss is negligible for streaming. The original lossless file remains in staging for download.

Seek Granularity

HLS handles random access at the segment level, not the codec level. Each 6-second .ts segment starts with a fresh AAC frame boundary.

  • Seek to 30s → player downloads the segment containing 30s
  • Granularity ≈ 6 seconds (segment duration)
  • Within a segment, audio decodes linearly (milliseconds for 6s of audio)

Shared Infrastructure

Audio reuses the same infrastructure as video:

ComponentShared?
Global chunk dedup pool (chunks/{hash}.ts)Yes
M3U8 bare-hash formatYes
rewriteM3u8() functionYes
Cache headers (immutable, 1-year TTL)Yes
Lambda function (hls-splitter)Yes (dispatched by type field)

Streaming Endpoint

GET /audio/:audioId/stream/:hash.m3u8

Same pattern as video — fetches m3u8 from S3, rewrites bare hashes to CDN URLs, returns with immutable cache headers.

Meta.json After Splitting

{
  "audioId": "...",
  "sampleRate": 44100,
  "channels": 2,
  "codec": "aac",
  "bitRate": 128000,
  "durationSec": 45.5,
  "streams": ["def789abc012"],
  "updatedAt": "2026-04-14T..."
}