TL;DR

1. The problem: why process media at all?

A modern phone produces a 12–48 megapixel photo of 2–6 MB (often in Apple's HEIC format) and 4K HDR video at 20–50 Mbit/s. Now think about who views it on Instagram: a user on a 5-year-old Android phone with a 720-pixel-wide screen on a congested 3G network in a rural area, a user on a new iPhone on Wi-Fi, and someone on a desktop browser that can't decode HEIC at all. Serving the original to all of them would be wrong in every way:

So every upload goes through a media processing pipeline that produces a set of safe, optimised, device-appropriate renditions, stores them in object storage, and lets the CDN serve the right one to each viewer.

🧠 Analogy Think of a photo lab and print shop. A customer drops off a raw negative. Before anything is printed, a clerk checks it's really film (validation), removes the customer's address label stuck on the envelope (EXIF stripping), turns it the right way up (orientation), and an inspector checks nothing illegal is on it (moderation). Then the lab produces wallet, postcard and poster prints (renditions) on different paper types (formats), a contact sheet of thumbnails, and for a home movie, copies on different tape qualities so it plays on any player (bitrate ladder). Big orders are split among many technicians (worker pools), urgent orders jump the queue (priorities), and the order slip tracks status: received → in the lab → ready for pickup (state machine).

2. The pipeline at a glance

Raw uploadS3 raw bucket Kafkamedia.uploaded Validatetype, size, decode Normalizestrip EXIF, rotate Renditionsresize ×N, JPEG/WebP/AVIF Video transcodeladder + HLS/DASH Thumbs + previewblurhash, poster frame Moderationhash match + ML Alt text (ML) Publishstate ✔
The media pipeline is a fan-out/fan-in DAG: sequential safety steps first, then parallel rendition, moderation and enrichment tasks, then a join that publishes the post.

The order matters: validate before decoding further (security), normalise before producing renditions (so every rendition is upright and metadata-free), run renditions and moderation in parallel, and only flip the post to PUBLISHED when the required outputs and checks are complete.

3. Image processing stages

3.1 Validation

3.2 EXIF stripping (privacy)

EXIF (Exchangeable Image File Format) metadata is embedded by cameras: camera make/model, lens, exposure, timestamps, sometimes the device serial number, a thumbnail of the original, and — critically — GPS latitude/longitude, accurate to a few metres. Publishing it would reveal where users live, where their children go to school, or where a vulnerable person is hiding.

$ exiftool IMG_4821.HEIC
Make                 : Apple
Model                : iPhone 15 Pro
Date/Time Original   : 2026:10:23 14:41:07
Orientation          : Rotate 90 CW
GPS Latitude         : 19 deg 4' 33.12" N        ← user's home, to ~3 m
GPS Longitude        : 72 deg 52' 40.56" E
Lens Model           : iPhone 15 Pro back triple camera 6.765mm f/1.78

The pipeline re-encodes every rendition from decoded pixels and writes no metadata except what's needed for correct display (e.g. an ICC colour profile). Anything useful (capture time, a location the user chose to tag) is stored as structured fields in the post's metadata DB, under the user's control — not hidden in the file. Re-encoding (rather than editing tags) also defeats metadata hidden in unusual places (XMP, IPTC, maker notes, trailing bytes after the image).

3.3 Orientation fix

Phone sensors capture in one fixed orientation. Instead of rotating pixels, the camera saves them as captured and sets the EXIF Orientation tag (values 1–8: normal, mirrored, rotated 90°/180°/270°, and mirrored+rotated). Viewers are supposed to apply it. If you strip EXIF without first applying the rotation, half your portrait photos show up sideways. So: read the Orientation tag, physically rotate/flip the pixels, then drop the tag. (Videos have a similar rotation flag in the container metadata.)

3.4 Resizing to multiple renditions

Generate a fixed set of widths covering common screen sizes and layouts — for example 1080, 750, 640, 480 and 320 px for the feed, plus small square crops (150–240 px) for profile grids and thumbnails. Instagram displays feed photos between 1.91:1 landscape and 4:5 portrait, 1080 px wide at most, so larger inputs are scaled down and out-of-range aspect ratios are cropped.

3.5 Format conversion: JPEG, WebP, AVIF

FormatTypical size vs JPEG at similar qualityEncode speedSupportNotes
JPEG (1992)1× (baseline)Very fastUniversalUse an optimising encoder (mozjpeg) and progressive mode so a blurry full image appears early.
WebP (2010)~25–35% smallerFastAll modern browsers, Android, iOS 14+Supports alpha and animation; good default for app and web.
AVIF (2019)~40–50% smallerSlow (5–20× JPEG)Modern browsers, Android 12+, iOS 16+Based on the AV1 video codec; supports HDR/wide gamut. Best bytes, most CPU.
HEIC/HEIF~40–50% smallerMediumApple devices; limited elsewhereCommon input format from iPhones; patent licensing limits use as an output.
JPEG XL~35–60% smaller; lossless JPEG recompression ~20%MediumLimited (Safari; others partial)Promising, not yet universal.

The API returns a set of candidate URLs (or the app requests by capability), and the client picks the best format it can decode and the smallest width ≥ its display width × pixel density. See Clients.

3.6 Quality vs compression trade-offs

Lossy encoders expose a quality knob (JPEG/WebP "q" 0–100, AVIF quantizer/CRF). Higher quality → bigger files; beyond ~85 the extra bytes are mostly invisible. Practical techniques:

 Illustrative sizes for one 1080×1350 photo (varies a lot with content):
   original HEIC from phone ............. 3.2 MB  (4032×5040)
   JPEG  q=85 progressive (mozjpeg) ...... 260 KB
   WebP  q=80 ............................ 180 KB   (-31%)
   AVIF  cq=28 ........................... 120 KB   (-54%)
   640 px WebP ...........................  70 KB
   150 px thumbnail WebP .................   6 KB
   BlurHash placeholder ..................  ~30 bytes (a text string!)

3.7 Thumbnails and placeholders (blurhash)

Even a 70 KB image takes a moment on a slow network. To avoid blank grey boxes while scrolling, the pipeline produces placeholders small enough to embed directly in the feed API's JSON, so they're shown instantly before any image request:

4. Video processing

4.1 Codecs vs containers

Two terms beginners often confuse:

Video codecs exploit redundancy in space (within a frame, like JPEG) and in time (consecutive frames are similar). Frames come in three kinds: I-frames (keyframes — self-contained, large), P-frames (store differences from previous frames using motion vectors) and B-frames (reference both past and future frames — smallest). A GOP (group of pictures) runs from one keyframe to the next. A player can only start decoding — or switch quality — at a keyframe, so keyframes must be placed at every segment boundary, aligned across all renditions (e.g. a keyframe every 2 s with 4 s segments).

CodecYearBitrate vs H.264 at equal qualityEncode costDevice supportLicensing
H.264 / AVC20031× (baseline)Low; hardware everywhereUniversal — every phone, browser, TVPatent pools (royalties)
H.265 / HEVC2013~30–50% less~2–10× H.264 in softwareApple devices, most newer Android hardware; patchy in browsersComplex, multiple patent pools
VP92013~30–40% less~5–10× H.264Chrome, Firefox, Android; YouTube's workhorseRoyalty-free (Google)
AV12018~30% less than VP9/HEVC, ~50% less than H.264High in software (SVT-AV1 made it practical); hardware decode in newer devicesModern browsers, newer phones with AV1 decodersRoyalty-free (Alliance for Open Media, incl. Meta)

The usual strategy is H.264 for everyone (guaranteed playback) plus a more efficient codec (VP9/AV1, or HEVC for Apple) for devices that support it — and, because those encodes are expensive, often only for videos that will be watched a lot (section 7).

4.2 The bitrate ladder

A bitrate ladder is the set of resolution/bitrate pairs a video is encoded into. Each "rung" is a complete rendition. Illustrative ladder for a vertical 9:16 Reel (actual values depend on content, codec and encoder):

RungResolution (9:16)H.264 video bitrateAV1 video bitrate (≈)30 s clip size (H.264, incl. 64–128 kbps audio)Typical network
240p240×426~300 kbps~150 kbps~1.4 MBPoor 3G
360p360×640~600 kbps~300 kbps~2.5 MB3G
480p480×854~1.0 Mbps~500 kbps~4.0 MBWeak 4G
720p720×1280~2.0 Mbps~1.0 Mbps~7.9 MB4G
1080p1080×1920~3.5–5 Mbps~2.0–2.5 Mbps~14–19 MBGood 4G / 5G / Wi-Fi

(Size check for 720p: 2.128 Mbps × 30 s ≈ 64 Mbit ≈ 8 MB.) Per-title / per-scene encoding — popularised publicly by Netflix — adapts the ladder to content: a talking head against a plain wall looks great at half the bitrate of a confetti-filled concert clip, so you measure quality (e.g. VMAF) and choose the cheapest bitrate per rung that hits the target.

# one rung of the ladder with ffmpeg (H.264, 720p, keyframe every 2 s @ 30 fps, 4 s fMP4 segments)
ffmpeg -i input.mov -vf "scale=720:1280" -c:v libx264 -preset slow -crf 23 \
       -maxrate 2000k -bufsize 4000k -g 60 -keyint_min 60 -sc_threshold 0 \
       -c:a aac -b:a 128k -f hls -hls_time 4 -hls_segment_type fmp4 \
       -hls_playlist_type vod 720p/index.m3u8

4.3 Segmenting: HLS and DASH

Each rendition is cut into short segments (typically 2–6 s) plus manifests that describe them:

#EXTM3U                                    <- master.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=428000,RESOLUTION=240x426,CODECS="avc1.4d4015,mp4a.40.2"
240p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2128000,RESOLUTION=720x1280,CODECS="avc1.4d401f,mp4a.40.2"
720p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=4128000,RESOLUTION=1080x1920,CODECS="avc1.640028,mp4a.40.2"
1080p/index.m3u8

#EXTM3U                                    <- 720p/index.m3u8
#EXT-X-TARGETDURATION:4
#EXT-X-MAP:URI="init.mp4"
#EXTINF:4.0,
seg_000.m4s
#EXTINF:4.0,
seg_001.m4s
#EXT-X-ENDLIST

Segments are small immutable files — perfect for CDN caching. Short-form apps also commonly use a single progressive MP4 per rendition fetched with HTTP Range requests for the very first seconds (fastest start), switching to ABR segments as playback continues.

4.4 Adaptive bitrate (ABR) playback

The player downloads the manifest, then fetches segments one at a time, choosing the rung for each segment based on measured throughput and how many seconds of video are buffered:

 bandwidth   ███████████▇▇▅▃▂▂▂▃▅▇██████        (Wi-Fi → walks into lift → back)
 rung chosen 1080 1080 1080 720 480 360 360 480 720 1080 1080
 buffer (s)  [4][8][12][14][12][9][8][8][10][12][14]
                                 ↑ buffer drains while bandwidth is low; player steps DOWN
                                   before the buffer empties, so playback never stalls

5. Orchestrating the pipeline

5.1 Kafka → worker pools → task DAG

When an upload completes, the Upload Service publishes media.uploaded to Kafka. A pipeline orchestrator consumes it and expands it into a DAG (directed acyclic graph) of tasks, each executed by a specialised worker pool:

Probe Split chunks Encode 1080p ×k Encode 720p ×k Encode 480p…240p Moderation Poster + preview Stitch +package Join / gate PUBLISHED parallel tasks on GPU / CPU pools
A video DAG: probe, split into chunks at keyframes, encode every chunk of every rung in parallel, stitch and package into HLS/DASH, and publish only when the gate's required inputs (renditions + moderation verdict) are present.

5.2 Retries and idempotency

Workers crash, spot instances vanish, storage calls time out; Kafka delivers at least once. Every task must therefore be safe to run twice:

5.3 Priorities and autoscaling

5.4 GPU and hardware encoders

Software encoders (x264, x265, libvpx, SVT-AV1) give the best compression per bit at slow presets but cost lots of CPU. Hardware encoders — GPU blocks such as NVIDIA NVENC and Intel Quick Sync, or dedicated ASICs — encode many streams in real time at a fraction of the power, at somewhat lower compression efficiency than the slowest software presets. Large platforms build their own: Google described its Argos video coding unit for YouTube, and Meta announced the MSVP (Meta Scalable Video Processor) ASIC for transcoding video-on-demand and live streams. A common split: hardware for the fast, high-volume first encodes; high-effort software encodes for the most-watched content where every saved bit is multiplied by millions of views.

6. Content moderation in the pipeline

Instagram must keep illegal and policy-violating content off the platform at the scale of hundreds of millions of uploads per day. Moderation is layered, from cheap and certain to expensive and nuanced:

6.1 Perceptual hashing: known illegal content

A cryptographic hash (SHA-256) changes completely if a single pixel changes, so re-saving, resizing or cropping an image defeats it. A perceptual hash instead summarises what the image looks like: visually similar images get hashes that differ in only a few bits, measured by Hamming distance (the number of differing bits).

# dHash - the simplest perceptual hash, to build intuition (64 bits)
def dhash(img):
    g = img.convert("L").resize((9, 8))          # grayscale, 9×8 pixels: throws away detail & colour
    px = list(g.getdata())
    bits = 0
    for row in range(8):
        for col in range(8):                     # is each pixel brighter than its right neighbour?
            left, right = px[row * 9 + col], px[row * 9 + col + 1]
            bits = (bits << 1) | (left > right)
    return bits

def is_match(h1, h2, threshold=10):
    return bin(h1 ^ h2).count("1") <= threshold  # Hamming distance: few differing bits = same picture

Production hashes are far more robust: PhotoDNA (developed by Microsoft and Dartmouth, licensed to platforms and used industry-wide) and Meta's open-sourced PDQ (256-bit, for photos) and TMK+PDQF (for video). Platforms check every upload against hash lists of known child sexual abuse material (CSAM) maintained with organisations such as NCMEC, and against shared databases of known terrorist content (e.g. GIFCT's hash-sharing database). A match is a near-certain verdict: the upload is blocked before publishing, evidence is preserved for the legally required report (US providers must report apparent CSAM to NCMEC), and the account is actioned by specialised teams. Hash matching is cheap (compute a hash, look up nearby hashes in an index), so it runs synchronously on every upload, and lists are re-applied retroactively when new hashes are added.

6.2 ML classifiers: new content

Hashes only catch known items. For everything else, ML models score each image (and sampled video frames, audio transcripts and on-image text via OCR) for policy areas: nudity/sexual content, graphic violence, hate symbols, self-harm, spam/scams, and more. Each model outputs a probability, and thresholds map scores to actions:

Score bandActionWhy
Very high confidence violationBlock or remove automaticallyPrecision is high enough that waiting for a human only spreads harm
Uncertain / borderlinePublish with reduced distribution (e.g. not eligible for Explore/recommendations) and/or queue for human reviewAvoid wrongly removing legitimate posts; humans decide
LowAllowMost content

Thresholds are tuned per policy and per surface: missing CSAM is unacceptable, so recall is pushed very high and humans absorb the extra false positives; for lower-severity policies, precision matters more. Heavy models run on GPU pools in parallel with transcoding so they add little to time-to-publish; some run asynchronously after publishing, with takedown if they fire later.

6.3 Human review queues

See Security & Privacy for abuse prevention more broadly.

7. Automatic alt text

Blind and low-vision users rely on screen readers (VoiceOver, TalkBack), which read an image's alt text. Most people never write one, so the pipeline generates it: object/scene-recognition or image-captioning models produce a description such as "Photo may contain: 2 people, smiling, beach, ocean, sunset". Facebook launched automatic alt text in 2016, and Instagram added automatic and custom alt text in 2018. The generated text is stored in the post's metadata (users can override it with their own), delivered in the API response, and — with privacy care — can be a signal for search and content understanding (see Search). It's an ML task that runs on the same GPU pool as moderation, off the critical path.

8. Cost optimisations

Media processing is one of the largest compute bills in a photo/video product, and its outputs drive storage and CDN bills. The big levers:

Is an AV1 encode worth it for one video? (all inputs are assumptions)
  AV1 encode cost: ~10 core-minutes ≈ 0.17 core-h × $0.03/core-h  ≈ $0.005
  Saves ~40% of bytes vs H.264: an 8 MB watch → saves ~3.2 MB per view
  CDN cost ≈ $0.01/GB → saving ≈ $0.000032 per view
  Break-even ≈ 0.005 / 0.000032 ≈ 160 views
  ⇒ encode AV1 for videos likely to exceed a few hundred views; skip it for the long tail.
  (Users on slow networks also get better quality at the same bitrate — a product win, not just cost.)

9. The post state machine

Many asynchronous steps must end in one consistent answer to "can people see this post?". A state machine stored with the post's metadata makes that explicit:

CREATED UPLOADING UPLOADED PROCESSING PUBLISHED DELETED ABANDONED FAILED IN_REVIEW REJECTED URL issued object stored event all required ✔ URL expired retry retries exhausted borderline approved violates hash match
The post lifecycle. Only PUBLISHED posts are returned by feed, profile and search APIs; every transition is a conditional update so duplicate or late events can't move a post backwards.

10. Numbers to reason with

All inputs are assumptions for practice (see Back-of-the-Envelope Estimation):

Photos: 100M/day ≈ 1,160/s average, ~3.5K/s at peak
  per photo: decode + orient + 5 sizes × (JPEG + WebP) + AVIF for the top sizes + hashes
  ≈ 1.5 core-seconds  →  peak ≈ 3.5K × 1.5 ≈ 5,000 cores busy
  outputs ≈ 12 files, ~0.5 MB total per photo → ~50 TB/day of image renditions

Videos: 20M/day, average 30 s
  cheap H.264 ladder (5 rungs) in software ≈ 2 core-minutes per video
  20M × 2 = 40M core-min/day ÷ 1,440 min ≈ 28,000 cores on average (peaks higher)
  AV1 for EVERY video at ~10× cost → ~280,000 cores  ✗
  AV1 for the ~5–10% of videos that get most watch time → +14K–28K cores  ✔
  (hardware encoders/ASICs cut these core counts dramatically)
  storage: ladder ≈ 30 MB + original ≈ 30 MB → ~1.2 PB/day for video

Latency targets (time from upload complete → visible):
  photo: ~1–2 s     30 s Reel: first rungs in ~5–15 s, remaining rungs/codecs minutes later

11. How Instagram uses the media pipeline

📱 Appresize, upload early
→pre-signed PUT
Object storageraw bucket
→media.uploaded
Media Processing WorkersDAG on CPU/GPU pools
→renditions
Object storageimmutable keys
→origin pull
CDNright rendition per device
  1. The app prepares media on-device (crop to the allowed aspect ratio, resize toward 1080 px, compress) and starts uploading via the Upload Service's pre-signed URL, often before the user finishes the caption.
  2. The Upload Service publishes media.uploaded; the post sits in PROCESSING.
  3. Media Processing Workers validate, strip EXIF/GPS, fix orientation, run hash matching and classifiers, produce image renditions or the video ladder + HLS/DASH segments, thumbnails, BlurHash/preview, and alt text — writing everything to object storage under immutable keys.
  4. When required outputs and blocking checks are done, the post becomes PUBLISHED and post.created triggers feed fan-out, search indexing and notifications.
  5. Viewers receive JSON with placeholders and signed CDN URLs per rendition; the app picks format and size, and the video player runs ABR over the CDN.
📸 In Instagram Publicly known details: Instagram displays feed photos at up to 1080 px wide with aspect ratios from 1.91:1 to 4:5, and resizes larger uploads; it introduced automatic alt text (plus custom alt text) in 2018; Meta has open-sourced the PDQ and TMK+PDQF perceptual hashing algorithms used to find matching photos and videos, and participates in industry hash-sharing for CSAM and terrorist content; Meta engineering has published about producing basic video encodings for everything while reserving advanced encodings such as AV1 for the most-watched content, and about its MSVP video transcoding ASIC. Early Instagram talks also emphasised perceived speed: uploading in the background while the user edits the caption and showing the post optimistically.

12. Trade-offs & common pitfalls

✅ What the pipeline buys

  • Fast loads and smooth playback on every device and network
  • Huge bandwidth savings (renditions + modern codecs)
  • Privacy (no GPS leaks) and security (malformed files rejected)
  • Platform safety via hashing, ML and human review
  • Accessibility via alt text

⚠️ What it costs

  • Large compute bill (video encoding dominates) and storage for many renditions
  • Time-to-publish latency and eventual consistency
  • Complex orchestration: DAGs, retries, partial failures
  • Codec support fragmentation across devices
  • Moderation errors either way: false positives anger users, false negatives cause harm

Common pitfalls

13. Real-world technologies

ToolWhat it doesWhen to use
libvips, sharp (Node), Pillow-SIMD, ImageMagickImage decode/resize/encodelibvips/sharp for high-throughput, low-memory pipelines; ImageMagick for breadth (sandbox it)
mozjpeg, libwebp, libavif, libjxlFormat encodersBest size/quality for each format
FFmpeg (+ x264, x265, libvpx, SVT-AV1)Swiss-army knife for probing, transcoding, packagingAlmost every video pipeline
NVIDIA NVENC, Intel QSV, custom ASICs (Meta MSVP, Google Argos)Hardware encodingHigh-volume, real-time or cost-sensitive transcoding
Shaka Packager, Bento4, GPACHLS/DASH/CMAF packaging, DRMProducing manifests and segments
AWS Elemental MediaConvert, Mux, Cloudflare StreamManaged video transcoding/streamingYou don't want to run encoding fleets
Cloudinary, imgix, ThumborOn-the-fly image transformation servicesLazy renditions via URL parameters
PhotoDNA, PDQ, TMK+PDQFPerceptual hashing for known harmful contentMandatory baseline for any user-generated media platform
AWS Rekognition, Google Cloud Vision, Hive, in-house modelsML classification, labels, OCR, captionsModeration and alt text without (or before) building your own models
Temporal, AWS Step Functions, Netflix ConductorDurable workflow/DAG orchestrationMulti-step pipelines with retries and state
BlurHash, ThumbHashCompact image placeholdersInstant visual placeholders in API responses

14. Interview corner

Design the photo processing pipeline for Instagram.

Client uploads directly to a raw bucket via a pre-signed URL; the Upload Service publishes media.uploaded and sets the post to PROCESSING. A worker pool consumes it: validate (magic bytes, size/pixel limits, sandboxed decode), apply EXIF orientation then strip all metadata, compute perceptual hashes and check against known-bad lists (block on match), produce renditions (e.g. 1080/640/320 px + thumbnails) in JPEG and WebP/AVIF with tuned quality, generate BlurHash and alt text, run classifiers. Outputs go to object storage under deterministic keys; a conditional state update to PUBLISHED triggers post.created for fan-out. Tasks are idempotent and retried with backoff; failures go to a DLQ and FAILED state. Pools autoscale on lag; critical renditions run in a high-priority lane.

How does adaptive bitrate streaming work and what does the backend have to produce?

The backend transcodes each video into a ladder of renditions (e.g. 240p–1080p at increasing bitrates), with keyframes aligned at fixed intervals, cuts each into 2–6 s segments, and writes HLS/DASH manifests listing variants and segments. The player downloads the manifest and then picks a rendition per segment based on measured throughput and buffer level, stepping down before the buffer empties and back up when bandwidth recovers. Segments are static files cached by the CDN.

Transcoding a 10-minute 4K video takes 20 minutes on one machine. How do you make it fast?

Split it at keyframes into chunks (say 10 s), encode every chunk of every rung in parallel across a worker pool (60 chunks × 5 rungs = 300 tasks), then stitch and package. Use hardware encoders for the first-available rungs, publish as soon as a playable subset (e.g. 360p/720p) is ready, and add higher rungs and advanced codecs later at lower priority. Ensure consistent encoder settings and closed GOPs so chunks join seamlessly.

How do you prevent known illegal images from ever being published?

Compute robust perceptual hashes (PhotoDNA, PDQ; TMK+PDQF for video) synchronously during processing, before the post can transition to PUBLISHED, and look them up against industry hash lists with a Hamming-distance threshold. A match blocks publication, preserves evidence, triggers the legally required report and account action through a specialised workflow. Re-scan existing content when new hashes arrive. ML classifiers and human review cover previously unseen content.

Video encoding costs are exploding. What do you do?

Encode a cheap baseline (H.264 ladder, possibly fewer rungs, hardware encoders) for every upload; reserve expensive codecs (AV1) and high-effort presets for videos predicted or observed to get high watch time, since views follow a power law; generate rare renditions lazily; use per-title encoding to avoid wasting bits on simple content; run batch work on spot capacity; dedupe re-uploads; and move to dedicated hardware encoders at scale. Measure with cost per watch-hour and quality metrics (VMAF), not just per-upload cost.

Why must the pipeline be idempotent, and how do you make it so?

Kafka delivers at least once and workers crash mid-task, so tasks will run more than once. Use deterministic output keys derived from (media_id, rendition, version), so re-running overwrites identical bytes; check for existing outputs before working; record task completion and post state with compare-and-set updates; and dedupe side effects (like emitting post.created) via the state transition — only the transition into PUBLISHED emits, and it can only happen once.

15. Check your understanding

Why does a portrait iPhone photo show sideways after a naive "strip all EXIF" step?

The phone stored the pixels in sensor orientation and recorded the needed rotation in the EXIF Orientation tag. Stripping the tag without first rotating the pixels loses that instruction. Fix: apply the orientation to the pixels, then strip.

What is the difference between a codec and a container? Give one example of each.

A codec compresses the media stream (H.264, AV1, AAC); a container packages encoded streams with timing/metadata into a file (MP4, WebM, MPEG-TS). The same MP4 container can hold H.264 or AV1 video.

A 720p rung is encoded at 2,000 kbps video + 128 kbps audio. How big is a 45 s clip?

2,128 kbps × 45 s = 95,760 kbit ≈ 95.8 Mbit ÷ 8 ≈ 12 MB.

Why must keyframes be aligned across all rungs of the ladder?

A player can only start decoding at a keyframe. ABR switches rendition at segment boundaries; if every rung has a keyframe at exactly the same timestamps (each segment starts with one), the player can switch cleanly. Misaligned keyframes cause glitches, stalls or the need to download overlapping data.

Two images: one is a re-saved, slightly cropped copy of the other. Will SHA-256 detect them as the same? Will PDQ?

SHA-256: no — any byte change produces a completely different hash. PDQ: likely yes — perceptual hashes of visually similar images differ in few bits, so the Hamming distance falls under the match threshold.

Using the break-even estimate in section 8, should you AV1-encode a Reel that will be watched ~40 times? ~10,000 times?

At ~160 views break-even: 40 views — no (encode cost exceeds bandwidth saved); 10,000 views — yes, savings are ~60× the encode cost, and viewers on slow networks get better quality.

A "media processed" event arrives twice for the same post; the user deleted the post in between. What should happen, and what mechanism guarantees it?

Nothing should change: the post must stay DELETED and no post.created should be emitted. A conditional state transition (… WHERE state = 'PROCESSING') fails for a DELETED post and for an already-PUBLISHED post, making the handler idempotent.

🎯 Key takeaways

Related lessons