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.

HEVC
The single most common codec causing silent processing failures in 2026 uploads
73%
Of "stream won't start" tickets trace back to a video processing failure, not a network issue
H.264
The only codec guaranteed compatible across every major streaming platform
7
Distinct pipeline stages where a video file can fail before it ever streams

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.

🔧 The 6-Stage Video Processing Pipeline
Each stage can fail independently — identifying the failed stage is the first diagnostic step
📥
Upload
File received and stored
Fails: size limit, timeout
🔍
Probe
Identify codec, resolution, streams
Fails: corrupted file, bad container
🧩
Demux
Split audio/video streams
Fails: missing moov atom
🎞️
Decode
Decompress frames for processing
Fails: unsupported codec (HEVC)
⚙️
Transcode
Re-encode to compatible format
Fails: timeout, resource limit
📡
Mux/Stream
Package into RTMP-ready output
Fails: profile/level mismatch

Problem 01 — HEVC/H.265 Not Detected or Rejected

CRITICAL
PROBLEM 01 · Codec Detection
HEVC/H.265 NOT DETECTED OR REJECTED
Video uploads successfully but fails to process, stream, or shows as corrupted — codec is the hidden cause
Codec Decode Stage Most Common 2026 Issue
Symptoms
  • 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
Root Causes
  • 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
Fix Priority
  • 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
🔧 Step-by-Step Fix
1
Always probe the actual codec — never trust the file extension. A file named video.mp4 can contain H.264, HEVC, or even ProRes depending on the source device. Run: ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of default=noprint_wrappers=1 input.mp4 This returns the actual video codec (e.g., codec_name=hevc). Build your pipeline's compatibility check on this output, not on file extension matching.
2
Add HEVC to your "needs transcode" detection list. If your pipeline has a list of formats considered "directly compatible" without transcoding, HEVC must NOT be on that list even though it's wrapped in a common .mp4 or .mov container. The container format and the codec inside it are independent — checking only the container extension is the single most common cause of this entire problem class. LIKELY_COMPAT_EXT = ['.mp4', '.mov'] // WRONG — checks extension only ACTUAL_CHECK: codec_name in ['h264', 'h265'] // hevc needs transcode regardless of extension
3
Transcode HEVC sources to H.264 before streaming. ffmpeg -i input_hevc.mp4 -c:v libx264 -preset medium -crf 20 -c:a aac -b:a 160k output_h264.mp4 This re-encodes the HEVC video to H.264, which every streaming platform and RTMP target accepts universally. CRF 20 preserves visual quality close to the source; adjust preset for speed/quality tradeoff (faster presets = quicker transcode, slightly lower quality per bitrate).
4
Verify FFmpeg has HEVC decode support compiled in. ffmpeg -decoders | grep hevc Should return 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

HIGH
PROBLEM 02 · Codec Compatibility
CODEC ACCEPTED BY PIPELINE, REJECTED BY PLATFORM
Video processes successfully but the platform refuses the stream or shows errors after ingest
Profile/Level Platform Requirements Mux Stage
Symptoms
  • 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
Root Causes
  • 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)
Fix Priority
  • 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
🔧 Step-by-Step Fix
1
Standardize all output to a universally-compatible encoding profile. The single safest configuration across every major platform (YouTube, Twitch, Kick, Facebook, TikTok): ffmpeg -i input.mp4 \ -c:v libx264 -profile:v main -level 4.1 -pix_fmt yuv420p \ -preset medium -crf 20 -g 60 -keyint_min 60 \ -c:a aac -b:a 160k -ar 48000 \ -movflags +faststart \ output_standardized.mp4 This forces H.264 main profile, 8-bit 4:2:0 color (yuv420p — the only color format guaranteed universal), AAC audio, and a 2-second keyframe interval at 30fps (-g 60 = 60 frames = 2 sec at 30fps).
2
Check for and convert 10-bit color sources. HDR content and some professional camera footage uses 10-bit color (yuv420p10le), which many platform decoders reject for live streaming even when the codec is otherwise H.264. Probe with: ffprobe -v error -select_streams v:0 -show_entries stream=pix_fmt -of default=noprint_wrappers=1 input.mp4 If the result contains "10le" or similar, the -pix_fmt yuv420p flag in the command above converts it to standard 8-bit during transcode.
3
Verify audio codec and force AAC if needed. Some source files use MP3, Opus, FLAC, or uncompressed PCM audio — none of which are universally accepted for live streaming. The -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

CRITICAL
PROBLEM 03 · Container Errors
"MOOV ATOM NOT FOUND" / CONTAINER CORRUPTION
File appears to upload completely but FFmpeg cannot read its structure — the file is structurally incomplete
MP4 Structure Demux Stage Upload Integrity
Symptoms
  • 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
Root Causes
  • 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
Fix Priority
  • 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)
🔧 Step-by-Step Fix
1
Understand what the moov atom is and why this happens. MP4 files store metadata (the "moov atom") describing the structure of the file — duration, codec info, track layout. Some recording devices write this atom at the END of the file after all video data, rather than at the beginning. If an upload is interrupted even slightly before completion, the moov atom — being the last bytes written — is the first thing lost, making the entire file unreadable despite having "most" of the data intact.
2
Verify uploaded file size matches the source exactly, byte for byte. Compare file sizes before and after upload. If they don't match exactly, the upload was truncated — re-upload is the only fix; the file cannot be repaired because the missing data (the moov atom) simply isn't present anywhere. ls -la original_file.mp4 uploaded_file.mp4 # Or use checksums for certainty: md5sum original_file.mp4 uploaded_file.mp4
3
For files that uploaded completely but still show this error, remux with faststart. If the file size matches and the error persists, the moov atom is present but positioned at the end of the file, which some processing tools require to be at the start. Remux (not re-encode) the file to relocate the moov atom to the beginning: ffmpeg -i input.mp4 -c copy -movflags +faststart output_fixed.mp4 The -c copy flag means no re-encoding happens — this is a fast, lossless structural fix that completes in seconds regardless of file length.
4
Implement chunked upload with integrity verification to prevent recurrence. For any upload pipeline accepting large video files, use resumable/chunked upload (uploading in pieces with the ability to resume after interruption) and verify a checksum after assembly. This prevents truncated files from ever entering your processing pipeline in the first place.

Problem 04 — ffprobe Stream Detection Failures

HIGH
PROBLEM 04 · ffprobe Errors
FFPROBE RETURNS EMPTY OR WRONG STREAM DATA
Your detection logic can't identify the video's properties — usually a command syntax problem, not a file problem
Probe Stage Command Syntax Stream Selection
Symptoms
  • 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
Root Causes
  • 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
Fix Priority
  • 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
🔧 Step-by-Step Fix
1
Test your exact ffprobe command directly in a terminal before debugging the application code. Most ffprobe failures are syntax issues, not file issues. Run the precise command your application uses against a known-good test file: ffprobe -v error -select_streams v:0 -show_entries stream=codec_name,width,height,pix_fmt -of json input.mp4 If this returns empty in the terminal too, the problem is the command, not your application's parsing logic.
2
Use JSON output format instead of default or csv for reliable programmatic parsing. The -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" } ] }
3
Always explicitly target stream index v:0 for the primary video stream. Files with multiple video streams (rare but possible — some screen recordings, multi-angle footage) will return ambiguous or unexpected results if you probe without specifying which stream index you want. -select_streams v:0 always targets the first video stream specifically.
4
Build a fallback path for when ffprobe genuinely returns nothing. If a file produces no usable probe data even with correct syntax, treat it as a corrupted/unsupported file rather than allowing your pipeline to silently default to "compatible" — this is what causes downstream failures that look like a completely different problem several stages later.
Automatic codec detection and transcoding handled for you

Skip the Pipeline Debugging.
Upload and Stream.

StreamKite automatically detects codec compatibility, transcodes HEVC and other incompatible formats, and validates your video before it ever reaches a streaming platform. No FFmpeg commands, no manual probing — just upload and stream 24/7.

Auto codec detection Recovery <5s 40+ Platforms
Get Your PassKey — From $4.80/mo
$4.80/mo · 3 stream slots · No auto-billing

Problem 05 — Thumbnail Generation Failures

MEDIUM
PROBLEM 05 · Thumbnail Pipeline
THUMBNAIL EXTRACTION FAILS OR PRODUCES BLACK/BLANK IMAGES
Video processes and streams correctly but the preview thumbnail is missing, black, or corrupted
Frame Extraction Stdout Piping Image Validation
Symptoms
  • 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
Root Causes
  • 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
Fix Priority
  • 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
🔧 Step-by-Step Fix
1
Extract the thumbnail frame at 25% of video duration, never at 0:00. The very first frame of a video is frequently black (fade-in transitions), a loading state, or otherwise unrepresentative. Calculate 25% of total duration and seek there instead: duration=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4) seek_time=$(echo "$duration * 0.25" | bc) ffmpeg -ss $seek_time -i input.mp4 -frames:v 1 -q:v 2 thumbnail.jpg
2
Write the extracted frame to a temporary file on disk, not directly to stdout. Piping binary image data through stdout in many scripting environments risks truncation, encoding corruption, or buffer issues — especially across process boundaries (e.g., Node.js spawning FFmpeg). Writing to a temp file and then reading the complete file is far more reliable: ffmpeg -ss $seek_time -i input.mp4 -frames:v 1 -q:v 2 /tmp/thumb_$(uuidgen).jpg # Then read the complete file, validate it, base64-encode if needed for API response
3
Validate the extracted file is a real JPEG before accepting it. Check the file's magic bytes (the first few bytes that identify file type) rather than just checking that a file exists with nonzero size: JPEG magic bytes: FF D8 FF (first 3 bytes) # In Python: with open('thumb.jpg', 'rb') as f: header = f.read(3) is_valid_jpeg = header == b'\xff\xd8\xff' A file that exists but fails this check should trigger a retry at a different timestamp, not be accepted as a valid thumbnail.
4
Add a retry mechanism with a fallback timestamp. If extraction at 25% fails (video shorter than expected, seek beyond duration, decode error at that specific frame), retry at 10% and then at 50% before giving up entirely. This handles edge cases like very short videos or videos with corrupted sections at specific timestamps.

Problem 06 — Frame Corruption / Decode Errors

MEDIUM
PROBLEM 06 · Decode Errors
VISUAL GLITCHES, GREEN FRAMES, OR DECODE ERRORS DURING PLAYBACK
The video streams but shows corrupted frames, green/garbled blocks, or decode error messages in logs
Decode Stage Bitstream Errors Source Corruption
Symptoms
  • 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
Root Causes
  • 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
Fix Priority
  • 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
🔧 Step-by-Step Fix
1
Identify whether corruption exists in the source or is introduced during transcoding. Play the original source file directly in VLC and seek to the timestamp where corruption appears in your processed output. If it's also corrupted in the source, the problem is upstream (bad recording, bad download) and transcoding cannot fix it — only conceal it. If the source plays cleanly at that timestamp, the corruption is being introduced by your transcode process.
2
Force constant frame rate (CFR) during transcode if the source is variable frame rate (VFR). Phone-recorded and screen-captured videos are frequently VFR, which can confuse some decoders and produce frame corruption or sync issues. Force CFR explicitly: ffmpeg -i input.mp4 -vsync cfr -r 30 -c:v libx264 -c:a aac output_cfr.mp4
3
Switch from GPU hardware decode to CPU software decode for the affected file. GPU decoders occasionally have specific frame-type or profile incompatibilities that produce visual corruption even when CPU decode of the identical file is clean. If using -hwaccel cuda or similar, remove the hardware acceleration flag and re-process with standard CPU decoding to test whether this resolves the corruption.
4
For genuine source corruption, use error concealment during re-encode. FFmpeg's error concealment options can paper over minor bitstream errors by interpolating from surrounding frames, though this won't perfectly restore lost data: ffmpeg -err_detect ignore_err -i input_corrupted.mp4 -c:v libx264 -c:a aac output_concealed.mp4 If corruption is severe, the only real fix is re-acquiring a clean copy of the source file.

Problem 07 — Transcoding Stuck or Hanging

HIGH
PROBLEM 07 · Transcode Hangs
TRANSCODING PROCESS HANGS OR NEVER COMPLETES
The transcode job starts but never finishes — no error, no progress, no output file
Resource Limits Process Management Queue Configuration
Symptoms
  • 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
Root Causes
  • 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
Fix Priority
  • 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
🔧 Step-by-Step Fix
1
Always include -nostdin when running FFmpeg from an automated pipeline. Without this flag, FFmpeg expects to read interactive keyboard input (for prompts like "overwrite file? y/n") and can hang indefinitely waiting for input that will never come in a non-interactive/scripted context. ffmpeg -nostdin -i input.mp4 -c:v libx264 output.mp4 -y The -y flag additionally auto-confirms overwrite prompts, which combined with -nostdin eliminates the most common cause of pipeline hangs.
2
Add a hard timeout that kills the process if it exceeds a reasonable duration. Calculate an expected maximum transcode time based on file duration and complexity, then enforce it: timeout 1800 ffmpeg -nostdin -i input.mp4 -c:v libx264 output.mp4 -y # Linux 'timeout' command kills the process after 1800 seconds (30 min) if still running In Node.js, Python, or other orchestration code, set an explicit timeout on the spawned child process and handle the timeout case as a failure requiring investigation, not a silent hang.
3
Check server memory and swap usage during the hang. Very high resolution sources (4K+) or extremely long videos can exhaust available memory during transcode, causing severe swap thrashing that looks like a "hang" but is actually the system struggling under memory pressure. free -h # Check available memory and swap usage while transcode is running vmstat 1 5 # Check for high swap activity (si/so columns) If memory is the bottleneck, reduce concurrent transcode jobs or upgrade server RAM.

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 MP4YesUniversal compatibility — the safe default to request from users
H.265 (HEVC) in MP4No — requires transcodeCommon from iPhones; must be detected and converted
VP9 in WebMNo — requires transcodeCommon from screen recording tools; needs conversion to H.264
ProRes / DNxHDNo — requires transcodeProfessional editing formats; far too large for streaming, always needs conversion
AAC audioYesUniversal — the safe default for audio
MP3 / Opus / PCM audioPartialSome platforms accept, others don't — safest to standardize to AAC always
10-bit color (HDR)No — requires transcodeMust 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 detectedCriticalDecodeProbe actual codec, transcode to H.264
Codec rejected by platformHighMux/StreamStandardize to main profile, 8-bit, AAC
Moov atom not foundCriticalDemuxVerify file size, remux with faststart
ffprobe returns emptyHighProbeFix command syntax, use JSON output
Thumbnail black/missingMediumPost-processExtract at 25% duration, validate magic bytes
Frame corruptionMediumDecodeForce CFR, try CPU decode instead of GPU
Transcode hangsHighTranscodeAdd -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.

Video processing handled automatically — every time

Stop Debugging FFmpeg.
Start Streaming Reliably.

StreamKite's video pipeline automatically detects codec compatibility, transcodes HEVC and incompatible formats, validates container integrity, and generates working thumbnails — before your stream ever goes live. Upload once, stream 24/7 to 40+ platforms.

Auto transcoding Recovery <5s Smart Scheduler 40+ Platforms From $1.60/stream
Get Your PassKey — Join StreamKite
$4.80/mo · 3 stream slots · $1.60/stream · PassKey emailed instantly · No subscription auto-billing