Skip to content

Question: understanding CapCut export encoding vs. FFmpeg output (upload speed difference) #4

@proditserbia

Description

@proditserbia

Hi @emosheeep,

I’ve been analyzing CapCut export files compared to videos generated via FFmpeg (NVENC) and noticed something interesting:
CapCut-exported videos — even with similar bitrate and resolution — upload to YouTube significantly faster than our FFmpeg outputs of the same duration and size.

For example:

CapCut export: 60 min, ~15 Mbps average, uploads in ~8 min

Our FFmpeg output: 60 min, ~15 Mbps (CBR_HQ, H.264 NVENC), uploads in ~25 min

When checking with MediaInfo, both files look similar in codec profile and bitrate.
But it seems CapCut might be doing something specific during muxing or metadata placement that helps YouTube’s processing (possibly “moov atom” optimization, GOP structure, or some proprietary flag).

Could you perhaps share any insight into how CapCut handles the final encode or if your tool has ever revealed additional mux parameters?

Here’s an example of the FFmpeg command we currently use:
def build_ffmpeg_cmd_filtergraph(files: List[Path], target_seconds: int, out_path: Path, cfg: JobConfig) -> List[str]:
w, h = VERT_W, VERT_H

vb_m = int(getattr(cfg, "bitrate_mbps", 15) or 15)
vb_m = max(12, min(22, vb_m))
vb = f"{vb_m}M"
if getattr(cfg, "youtube_mode", True):
    # smiren burst za predvidljiv CBR (brži YT processing)
    maxrate = f"{int(vb_m * 1.25)}M"
    bufsize = f"{vb_m * 2}M"
else:
    maxrate = f"{vb_m * 3}M"
    bufsize = f"{vb_m * 2}M"

cmd = [cfg.ffmpeg_path, "-hide_banner", "-y", "-loglevel", "info", *IN_OPTS]
for f in files:
    cmd += ["-i", str(f)]

# --- video chain (GPU/CPU auto) ---
# VIDEO chain: GPU scale (cover) -> CPU crop (center) -> SAR
if USE_CUDA:
    vchain = (
        "hwupload_cuda,"
        f"scale_cuda={VERT_W}:{VERT_H}:force_original_aspect_ratio=increase,"
        "hwdownload,format=yuv420p,"
        f"crop={VERT_W}:{VERT_H}:x=(in_w-out_w)/2:y=(in_h-out_h)/2,"
        "setsar=1:1,setpts=PTS-STARTPTS"
    )
else:
    vchain = (
        f"scale={VERT_W}:{VERT_H}:force_original_aspect_ratio=increase,"
        f"crop={VERT_W}:{VERT_H}:x=(in_w-out_w)/2:y=(in_h-out_h)/2,"
        "format=yuv420p,setsar=1:1,setpts=PTS-STARTPTS"
    )

# --- audio chain (48 kHz za YT) ---
achain = (
    "aformat=sample_fmts=fltp:sample_rates=48000:channel_layouts=stereo,"
    "aresample=48000:async=1:min_comp=0.001:first_pts=0,asetpts=PTS-STARTPTS"
)

# --- build all filter chains ---
chains = []
for i in range(len(files)):
    chains.append(f"[{i}:v]{vchain}[v{i}]")
    chains.append(f"[{i}:a]{achain}[a{i}]")

concat_inputs = "".join(f"[v{i}][a{i}]" for i in range(len(files)))
concat = f"{concat_inputs}concat=n={len(files)}:v=1:a=1[v][a]"
post = "[v]fps=30[vf];[a]anull[af]"

filter_complex = ";".join(chains + [concat, post])

# --- final command ---
cmd += [
    "-filter_complex", filter_complex,
    "-map", "[vf]", "-map", "[af]",
    "-t", str(target_seconds),

    "-fps_mode", "cfr",
    "-vsync", "cfr",
    "-max_muxing_queue_size", "4096",

    "-colorspace", "bt709", "-color_primaries", "bt709", "-color_trc", "bt709", "-color_range", "tv",

    "-c:v", "h264_nvenc",
    "-preset", "p2",
    "-rc", "cbr",
    "-b:v", vb,
    "-maxrate", maxrate,
    "-bufsize", bufsize,
    "-g", "60",
    "-bf", "2",
    "-forced-idr", "1",
    "-sc_threshold", "0",
    "-spatial-aq", "1", "-aq-strength", "8",
    "-profile:v", "main",
    "-level", "5.0",
    "-tag:v", "avc1",
    "-bsf:v", "h264_metadata=aud=insert",

    "-c:a", "aac",
    "-ar", "44100",
    "-b:a", "192k",

    # Progressive MP4 sa moov napred → YouTube-friendly
    "-movflags", "+faststart",
    "-use_editlist", "0",
    "-brand", "mp42",

    "-write_tmcd", "0",
    "-map_metadata", "-1",
    "-map_chapters", "-1",
    "-avoid_negative_ts", "make_zero",
    "-muxpreload", "0",
    "-muxdelay", "0",
    "-video_track_timescale", "30000",
    "-dn",
    str(out_path)
]
return cmd

We’re trying to replicate CapCut’s export structure to improve upload speed and processing time on YouTube.

Any insights, tips, or references would be greatly appreciated!

Thanks a lot for your time and great work on this project 🙌
— Marko

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions