What This Guide Covers
Before a video ever reaches a live audience, it passes through a processing pipeline — probing, demuxing, decoding, scaling, transcoding, and re-muxing into a streamable format. Every one of those stages can fail silently or loudly, and when it does, the result looks like "the stream won't start" or "the video looks wrong" with no obvious cause. The actual cause is almost always somewhere in this invisible processing chain, not in OBS, not in your internet connection, and not in the platform you're streaming to.
This guide treats video processing as its own category of streaming problem — separate from network issues, encoder overload, or platform configuration. Every problem here happens before or during the conversion of your source video into a stream-ready format, and every fix targets that specific stage of the pipeline.
The Video Processing Pipeline — Where Failures Happen
Every uploaded video destined for streaming passes through the same sequence of operations, whether the pipeline is your own FFmpeg script or a managed service. Understanding which stage failed determines which fix applies.
Problem 01 — HEVC/H.265 Not Detected or Rejected
- Upload succeeds but processing never completes
- Video plays fine locally (VLC/QuickTime) but fails in pipeline
- ffprobe returns no video stream information
- File extension is .mp4 but codec inside is unexpected
- iPhone-recorded videos fail more often than others
- iPhone/modern cameras default to HEVC inside .mp4 containers
- File extension doesn't guarantee codec — .mp4 can contain HEVC
- Pipeline's codec allowlist checks extension, not actual codec
- ffprobe command missing correct stream selection flags
- Older FFmpeg builds lack HEVC decode support
- First: Probe actual codec, not file extension
- Second: Transcode HEVC to H.264 before pipeline
- Third: Fix ffprobe stream selection flags
- Fourth: Update FFmpeg build with HEVC support
codec_name=hevc). Build your pipeline's compatibility check on this output, not on file extension matching.hevc and ideally hevc_cuvid or hevc_qsv if GPU-accelerated decode is available. If no HEVC decoder is listed, your FFmpeg build needs to be recompiled with libx265/hevc support or replaced with a build that includes it (most modern static builds from BtbN or similar sources include full codec support).This problem disproportionately affects content from iPhones (which record HEVC by default since iOS 11) and many Android flagships. If your platform accepts user uploads from mobile devices, assume a meaningful percentage will be HEVC and build automatic detection and transcoding into your pipeline from day one rather than treating it as an edge case.
Problem 02 — Codec Incompatible with Streaming Platform
- Stream connects but platform shows "not optimized" warning
- Platform dashboard shows stream as offline despite active push
- Some platforms accept the stream, others reject it from the same source
- Audio plays but video doesn't, or vice versa
- H.264 profile set to "high" instead of "main"
- B-frames configuration incompatible with platform decoder
- Audio codec is not AAC (e.g., MP3, Opus, PCM)
- Color space / pixel format mismatch (10-bit vs 8-bit)
- First: Force H.264 profile to "main"
- Second: Force audio codec to AAC
- Third: Convert 10-bit to 8-bit color depth
- Fourth: Standardize keyframe interval to 2 seconds
-pix_fmt yuv420p flag in the command above converts it to standard 8-bit during transcode.-c:a aac flag in the standardization command forces conversion. Confirm with:
ffprobe -v error -select_streams a:0 -show_entries stream=codec_name -of default=noprint_wrappers=1 input.mp4
Problem 03 — Container Format / Moov Atom Errors
- FFmpeg/ffprobe error: "moov atom not found"
- File size matches expected but won't process
- File plays in some players but not others
- Happens specifically after large or slow uploads
- Upload was interrupted before the moov atom was written
- Source file recorded with moov atom at end (common with phones)
- Chunked upload assembly missing final bytes
- File transfer over unstable connection truncated the file
- First: Verify file size matches source exactly
- Second: Re-upload with resumable/chunked transfer
- Third: Use faststart remux to relocate moov atom
- Fourth: Add upload integrity checksums (MD5/SHA)
-c copy flag means no re-encoding happens — this is a fast, lossless structural fix that completes in seconds regardless of file length.Problem 04 — ffprobe Stream Detection Failures
- ffprobe command returns nothing or an empty array
- Codec detection logic always falls to a default/fallback value
- Works for some files, silently fails for others
- "needsTranscode" flag missing from API response entirely
- Incorrect -select_streams syntax (common copy-paste error)
- Missing -show_entries flag specification
- Output format not parsed correctly by calling code
- Multiple video streams confusing index-based selection
- First: Test ffprobe command standalone in terminal
- Second: Use JSON output format for reliable parsing
- Third: Explicitly select stream index v:0
- Fourth: Add fallback handling for probe failures
-of json flag produces structured output that's far less error-prone to parse than ffprobe's default text format, which has inconsistent spacing and formatting across FFmpeg versions.
{
"streams": [
{
"codec_name": "h264",
"width": 1920,
"height": 1080,
"pix_fmt": "yuv420p"
}
]
}
-select_streams v:0 always targets the first video stream specifically.Problem 05 — Thumbnail Generation Failures
- Thumbnail shows as black rectangle or broken image icon
- Thumbnail file exists but has 0 bytes or near-zero size
- Works for short videos, fails for longer ones
- Intermittent — works sometimes, fails other times for same file
- Extracting frame at 0:00 — often black/fade-in frame
- Piping frame data directly to stdout causes truncation
- No validation that extracted data is a real image
- Seek position beyond actual video duration
- First: Extract frame at 25% duration, not 0:00
- Second: Write to temp file, not stdout pipe
- Third: Validate JPEG magic bytes before accepting
- Fourth: Add retry with different timestamp on failure
Problem 06 — Frame Corruption / Decode Errors
- Green or garbled blocks appear in specific sections of video
- FFmpeg logs show "concealing errors" or "corrupt decoded frame"
- Corruption appears at the same timestamp every loop
- Worse after transcoding than in the original file
- Source file has genuine bitstream corruption from recording
- Incomplete download/transfer introduced byte errors
- GPU hardware decode failing on specific frame types
- Variable frame rate source causing decoder confusion
- First: Re-encode with error concealment enabled
- Second: Switch from GPU to CPU decode for affected file
- Third: Force constant frame rate during transcode
- Fourth: Re-acquire source file if corruption persists
-hwaccel cuda or similar, remove the hardware acceleration flag and re-process with standard CPU decoding to test whether this resolves the corruption.Problem 07 — Transcoding Stuck or Hanging
- FFmpeg process shows in process list but CPU usage is near zero
- No progress in output, no error in logs
- Job queue shows "processing" indefinitely
- Server resource monitor shows memory climbing without bound
- FFmpeg waiting on stdin input that will never arrive
- Output pipe buffer full, nothing reading the other end
- Insufficient memory causing swap thrashing
- No timeout configured on the transcode process
- First: Add explicit -nostdin flag to FFmpeg command
- Second: Add hard timeout with automatic process kill
- Third: Ensure output is consumed, not piped to nowhere
- Fourth: Check available memory/swap on processing server
-y flag additionally auto-confirms overwrite prompts, which combined with -nostdin eliminates the most common cause of pipeline hangs.Prevention — Upload Requirements That Avoid These Problems
Every problem in this guide is reactive — diagnosing and fixing a failure after it occurs. The more efficient approach for any platform accepting user-uploaded video is to prevent these failures from reaching the processing pipeline at all, through clear upload requirements and pre-flight validation.
| Codec/Format | Stream-Ready Without Transcode? | Notes |
|---|---|---|
| H.264 (AVC) in MP4 | Yes | Universal compatibility — the safe default to request from users |
| H.265 (HEVC) in MP4 | No — requires transcode | Common from iPhones; must be detected and converted |
| VP9 in WebM | No — requires transcode | Common from screen recording tools; needs conversion to H.264 |
| ProRes / DNxHD | No — requires transcode | Professional editing formats; far too large for streaming, always needs conversion |
| AAC audio | Yes | Universal — the safe default for audio |
| MP3 / Opus / PCM audio | Partial | Some platforms accept, others don't — safest to standardize to AAC always |
| 10-bit color (HDR) | No — requires transcode | Must convert to 8-bit yuv420p for universal live streaming compatibility |
- Validate immediately on upload, not at stream time. Run ffprobe as the very first step after upload completes — before the file is queued for any further processing. Surface codec/format issues to the user or trigger automatic transcoding immediately, rather than discovering the problem hours later when the stream is supposed to start.
- Standardize everything to one known-good format. Rather than trying to support every possible codec combination a user might upload, transcode everything to a single standardized output profile (H.264 main, AAC, yuv420p, 2-second keyframes) immediately on ingest. This eliminates an entire category of "works for some files, not others" bugs.
- Never trust file extensions for compatibility decisions. Every compatibility check in your pipeline should be based on actual probed codec/format data, never on the filename's extension.
- Build automatic retry and fallback into every pipeline stage. Thumbnail extraction, transcoding, and probing should each have a defined fallback behavior for failure cases rather than allowing a single stage failure to silently break the entire pipeline.
Quick Reference — All Problems at a Glance
| Problem | Severity | Pipeline Stage | Fastest Fix |
|---|---|---|---|
| HEVC not detected | Critical | Decode | Probe actual codec, transcode to H.264 |
| Codec rejected by platform | High | Mux/Stream | Standardize to main profile, 8-bit, AAC |
| Moov atom not found | Critical | Demux | Verify file size, remux with faststart |
| ffprobe returns empty | High | Probe | Fix command syntax, use JSON output |
| Thumbnail black/missing | Medium | Post-process | Extract at 25% duration, validate magic bytes |
| Frame corruption | Medium | Decode | Force CFR, try CPU decode instead of GPU |
| Transcode hangs | High | Transcode | Add -nostdin flag and hard timeout |
🔧 Video Processing Pipeline Health Checklist
- Codec detection based on ffprobe output, never file extension
- HEVC explicitly flagged for mandatory transcoding
- Standard output profile defined — H.264 main, 8-bit, AAC, 2s keyframes
- Upload integrity verified with size/checksum comparison
- ffprobe commands use JSON output for reliable parsing
- Thumbnail extraction at 25% duration, not 0:00
- Magic byte validation on all extracted thumbnail images
- -nostdin flag present on all automated FFmpeg calls
- Hard timeout configured on every transcode job
- Fallback/retry logic defined for every pipeline stage
Video processing failures are some of the most frustrating streaming problems precisely because they're invisible — the failure happened minutes or hours before the stream was supposed to go live, in a stage of the pipeline most streamers never see directly. Understanding the six-stage pipeline and knowing exactly which stage produces which symptom turns an opaque "it just doesn't work" into a specific, fixable problem. Most of these failures trace back to the same root cause repeated across different symptoms: trusting file extensions or assumptions instead of actually probing the real properties of the video file.