Files
Video-Processing-Scripts/video-processing-scripts/Clone-Video.md
T

25 KiB

Clone-Video.ps1

Clones (copies) a video file using ffmpeg with optional frame range selection and sequence extraction. Reads MP4 (H.264), WebM and Matroska (MKV) sources — including YouTube/Google DASH downloads encoded as VP9 + Opus/AAC — and writes MP4, WebM, or MKV output depending on the -Target file extension. Also builds a video from a directory of sequentially-named PNG files (e.g. frame_0000.png) when -Source points to a folder, or from a single still image (.png, .jpg, .jpeg, .webp, .bmp, .tiff, .tif, .gif) for the duration of a provided audio file when -Source is an image and -Audio is specified.

Synopsis

When Source is a directory containing PNG files with sequence numbers:

  • PNG files are sorted alphabetically (ascending) to form the frame list
  • -StartFrame / -EndFrame filter the list by 0-based index before encoding
  • If -Reverse is set, the list is sorted descending after any frame range filtering
  • If -Size is not specified, the dimensions of the first PNG are used
  • If -FPS is not specified, 24 fps is used
  • If -CRF is not specified, CRF 8 is used
  • If -Audio is not specified, the output has no audio
  • -LTX applies the same closest-bucket logic as for video sources
  • -Crop applies the same crop rules as for video sources
  • -Sequence and -Metadata are not allowed in this mode and produce an error

When Source is a supported image file (.png, .jpg, .jpeg, .webp, .bmp, .tiff, .tif, .gif) and -Audio is provided:

  • The image is looped as every frame for the duration of the audio file
  • -NoAudio is not allowed in this mode
  • If -Size is not specified, the source image dimensions are used
  • If -FPS is not specified, 24 fps is used
  • If -CRF is not specified, CRF 8 is used
  • -Size, -FPS, -CRF, -LTX and -Crop are honoured
  • -StartFrame, -EndFrame, -Frames, -Sequence, -Metadata and -Reverse are ignored

When -Frames is provided, it defines a frame range using a flexible format and is mutually exclusive with -StartFrame, -EndFrame and -Sequence.

When Sequence is provided (video source mode):

  • If EndFrame is -1: Extracts the last frame to PNG, then clones video excluding the last frame
  • If EndFrame is not -1: Extracts the frame at EndFrame to PNG, then clones video from StartFrame to EndFrame-1

The output container is chosen from the -Target extension (.mp4, .webm, or .mkv; defaults to .mp4 when no recognised extension is given). Each container has its own codec profile — the script stream-copies streams that are already compatible and transcodes anything else to the container's preferred codecs. See Output containers for the full matrix.

Parameters

  • -Source (required): Path to an input video file or a directory containing PNG files with sequence numbers (e.g. frame_0000.png) or a supported image file. For video files — accepted containers: .mp4, .webm, .mkv; if the path has no extension the script tries .mp4, .webm, then .mkv in turn. For PNG sequence directories — see PNG Image Sequence Mode. For supported image files — see Image-to-Video Mode.
  • -Target (required): Path to output video file. The extension picks the output container: .mp4 (H.264/AAC), .webm (VP9/Opus), or .mkv (H.264/AAC, permissive stream-copy). If the extension is missing or unrecognised, .mp4 is appended and the MP4 profile is used.
  • -StartFrame (optional): 0-based frame index to start cloning from (default: 0). Mutually exclusive with -Frames.
  • -EndFrame (optional): 0-based frame index to end cloning at, use -1 for end (default: -1). Mutually exclusive with -Frames.
  • -Frames (optional): 0-based frame range to clone. Mutually exclusive with -StartFrame, -EndFrame, and -Sequence. Supported formats:
    • start-end: Clone frames from start to end (e.g., "100-500"). Both ends must lie inside the video, and start must not be greater than end. Otherwise the script stops with exit code 1.
    • positive_number: Clone frames from 0 to that number (e.g., "50"). A single number past the last frame is clamped to the last frame ("150" on a 100-frame video gives frames 0 to 99).
    • -N: Clone the last N frames (e.g., "-10" clones frames 90 to 99 of a 100-frame video; "-1" clones the last frame only)
    • %: The last frame index. Alone ("%") it selects only the last frame. In a range (e.g., "10-%") it is the end of the range
    • -% is not supported and is rejected with exit code 1
    • all: Clone all frames (same as "0-%")
  • -Sequence (optional): Path to PNG file for extracted frame (.png extension added if missing)
  • -Size (optional): Size of the output video. Uses the input video's size if not specified. Possible values: 320p, 384p, 480p, 720p, 900p, 1080p, HD, 1440p, 2K, 2160p, 4K, or custom size in format width:height. For standard named sizes the output preserves the source aspect ratio: the reference height is used as the short-side target and the other dimension is computed from the source AR — landscape/square sources use the reference as the output height (width computed), portrait sources use it as the output width (height computed). Custom sizes (width:height) are applied exactly as given. Forces a video re-encode, since scaling requires re-encoding.
  • -FPS (optional): Frames per second for the output video. Uses the input video's FPS if not specified. Forces a video re-encode, since changing frame rate requires re-encoding.
  • -NoAudio: Exclude audio from cloned video. Forces a video re-encode, since removing the audio stream requires re-muxing the output file.
  • -Audio (optional): Path to an alternate audio file to use for the target video. When specified, this audio replaces the source video's audio. Cannot be used with -NoAudio. When -Source is a supported image file, -Audio determines the output video duration.
  • -CRF (optional): Constant Rate Factor for video encoding (0-51, lower is better quality). When specified, overrides both the video bitrate and the default -crf 8 value. Only used when re-encoding is required
  • -Metadata: Copy available JSON metadata from source video file to target video file
  • -Reverse: Re-encode the video so it plays backward (last selected frame to first). Honours all other parameters (frame range, -Size, -FPS, -CRF, target container, etc.). Audio is not reversed — it is processed exactly as without -Reverse (trimmed/replaced/copied as applicable) and plays forward alongside the reversed video. Forces a video re-encode, since stream-copy is incompatible with the reverse filter.
  • -LTX: Override the dimensions chosen by -Size with the closest LTX-2 (Wan2GP by DeepBeepMeep) canonical resolution for the source's aspect ratio. Only valid when -Size is one of HD, 1080p, 720p or 480p. The script probes the source's width/height, computes its aspect ratio, and picks the bucket whose ratio is numerically closest. Forces a video re-encode (same as -Size). The buckets per -Size are:
    • HD / 1080p: 1920x1088 (16:9), 1088x1920 (9:16), 1664x960 (21:9), 960x1664 (9:21).
    • 720p: 1280x704 (16:9), 704x1280 (9:16), 1088x576 (21:9), 576x1088 (9:21).
    • 480p: 832x448 (16:9), 448x832 (9:16), 896x512 (4:3), 512x896 (3:4).
  • -Crop: Obtain the dimensions selected by -Size (including -LTX buckets) by center-cropping the source instead of resizing it, so the original aspect ratio of the picture content is kept (no stretching or scaling). Requires -Size. Without -Crop the source is resized (default behaviour). Honoured in all modes (video, PNG sequence and image-to-video). Forces a video re-encode (same as -Size). See Crop rules.

Crop rules

When -Crop is present, the requested output size is first computed exactly as without -Crop (named presets apply the short-side AR rule, custom width:height is used as given, -LTX picks its bucket). The source is then cropped around its center instead of being scaled:

  • A dimension is cropped only when the source is bigger than the requested size in that dimension.
  • A dimension where the source is equal or smaller is left untouched — no upscaling or padding is applied, so the output can be smaller than the requested size.
  • Cropped dimensions are rounded down to an even number (required by yuv420p).
Source -Size Without -Crop (resize) With -Crop
1080x1080 960:1080 960x1080 (squeezed horizontally) 960x1080 (cropped horizontally)
1080x1080 720:720 720x720 (scaled down) 720x720 (cropped horizontally and vertically)
1080x1080 1920:720 1920x720 (stretched) 1080x720 (cropped vertically only)

Image-to-Video Mode

When -Source is a supported image file and -Audio is provided, the script builds a video of the static image lasting for the duration of the audio file, bypassing all video-probing logic.

Supported image formats

.png, .jpg, .jpeg, .webp, .bmp, .tiff, .tif, .gif

Defaults vs. overrides

Parameter Default when omitted Can be overridden?
-FPS 24 Yes — any positive value
-CRF 8 Yes — any value 0-51
-Size Source image dimensions (even-padded) Yes — any named or custom size

Dimension rules

  • If -Size is a named preset (720p, 1080p, etc.) without -LTX, the short-side AR rule from video mode applies using the source image's dimensions.
  • If -Size is a custom width:height, it is applied exactly.
  • If -Size is omitted, the source image's width/height are used directly (padded to an even number if needed).
  • -LTX picks the closest LTX-2 bucket from the source image's aspect ratio. Only valid with -Size HD, 1080p, 720p or 480p.
  • -Crop center-crops the source image to the computed size instead of resizing it (see Crop rules).

Restrictions

  • -NoAudio is not allowed; audio is required in this mode.
  • -StartFrame, -EndFrame, -Frames, -Sequence, -Metadata and -Reverse are ignored.

Output container

The output container is determined by the -Target extension (.mp4, .webm, .mkv), using the same codec profiles as for video sources. For libx264 targets (MP4/MKV), -tune stillimage is applied to optimise encoding for static content.

PNG Image Sequence Mode

When -Source is a directory the script builds a video from the PNG files found inside, bypassing all video-probing logic. The behaviour is fully driven by the parameters below.

Frame selection

-StartFrame and -EndFrame are 0-based indices into the alphabetically-sorted PNG list (not time-codes). The default range covers all PNG files. -Frames can be used instead of -StartFrame/-EndFrame to specify the range using the flexible format described in the Parameters section.

Ordering

The list is always built in ascending alphabetical order first. -Reverse then reverses it after the frame range has been applied, so the first frame in the output is list[EndFrame] and the last is list[StartFrame].

Defaults vs. overrides

Parameter Default when omitted Can be overridden?
-FPS 24 Yes — any positive value
-CRF 8 Yes — any value 0-51
-Size First PNG dimensions (even-padded) Yes — any named or custom size
Audio No audio (-an) Yes — provide -Audio

Dimension rules

  • If -Size is a named preset (720p, 1080p, etc.) without -LTX, the short-side AR rule from video mode applies using the first PNG's dimensions.
  • If -Size is a custom width:height, it is applied exactly (as with video mode).
  • If -Size is omitted, the first PNG's width/height are used directly (padded to an even number if needed).
  • -LTX picks the closest LTX-2 bucket from the first PNG's aspect ratio. Only valid with -Size HD, 1080p, 720p or 480p.
  • -Crop center-crops the PNG frames to the computed size instead of resizing them (see Crop rules).

Unsupported parameters

-Sequence and -Metadata are not valid in PNG sequence mode. Specifying either produces a terminating error before any file I/O occurs.

Output container

The output container is still determined by the -Target extension (.mp4, .webm, .mkv), using the same codec profiles as for video sources.

Examples

Clone entire video:

.\Clone-Video.ps1 -Source video.mp4 -Target output.mp4

Clone specific frame range:

.\Clone-Video.ps1 -Source video.mp4 -Target clip.mp4 -StartFrame 100 -EndFrame 500

Clone frame range using -Frames:

.\Clone-Video.ps1 -Source video.mp4 -Target clip.mp4 -Frames "100-500"

Clone from frame 0 to frame 50:

.\Clone-Video.ps1 -Source video.mp4 -Target clip.mp4 -Frames "50"

Clone last 10 frames:

.\Clone-Video.ps1 -Source video.mp4 -Target clip.mp4 -Frames "-10"

Clone from frame 10 to last frame:

.\Clone-Video.ps1 -Source video.mp4 -Target clip.mp4 -Frames "10-%"

Extract last frame and clone without it:

.\Clone-Video.ps1 -Source video.mp4 -Target output.mp4 -Sequence last_frame.png
# Extracts last frame to last_frame.png
# Clones all frames except the last one

Extract specific frame and clone up to it:

.\Clone-Video.ps1 -Source video.mp4 -Target clip.mp4 -EndFrame 100 -Sequence frame100.png
# Extracts frame 100 to frame100.png
# Clones frames 0-99 (excluding frame 100)

Clone with resized output:

.\Clone-Video.ps1 -Source video.mp4 -Target output.mp4 -Size 720p

Clone with custom resolution:

.\Clone-Video.ps1 -Source video.mp4 -Target output.mp4 -Size 1280:720

Clone with cropped output (no resizing):

.\Clone-Video.ps1 -Source square.mp4 -Target output.mp4 -Size 960:1080 -Crop
# A 1080x1080 source is center-cropped horizontally to 960x1080.

Clone with output cropped on both dimensions:

.\Clone-Video.ps1 -Source square.mp4 -Target output.mp4 -Size 720:720 -Crop
# A 1080x1080 source is center-cropped horizontally and vertically to 720x720.

Clone with different FPS:

.\Clone-Video.ps1 -Source video.mp4 -Target output.mp4 -FPS 24

Clone with resized output and different FPS:

.\Clone-Video.ps1 -Source video.mp4 -Target output.mp4 -Size 1080p -FPS 30

Clone without audio:

.\Clone-Video.ps1 -Source video.mp4 -Target silent.mp4 -NoAudio

Clone with alternate audio:

.\Clone-Video.ps1 -Source video.mp4 -Target output.mp4 -Audio music.mp3
# Replaces the video's audio with audio from music.mp3

Clone frame range with alternate audio:

.\Clone-Video.ps1 -Source video.mp4 -Target clip.mp4 -StartFrame 100 -EndFrame 500 -Audio soundtrack.wav
# Clones frames 100-500 and uses audio from soundtrack.wav (trimmed to match video duration)

Clone using the closest LTX-2 720p bucket:

.\Clone-Video.ps1 -Source video.mp4 -Target ltx720.mp4 -Size 720p -LTX
# A 16:9 source is rescaled to 1280x704, a 9:16 source to 704x1280,
# a 21:9 source to 1088x576, a 9:21 source to 576x1088.

Clone using the closest LTX-2 1080p bucket:

.\Clone-Video.ps1 -Source phone.mp4 -Target ltx1080.mp4 -Size 1080p -LTX
# A 1080x1920 (9:16) phone clip is rescaled to 1088x1920.

Clone with custom CRF value:

.\Clone-Video.ps1 -Source video.mp4 -Target output.mp4 -Size 1080p -CRF 18
# Uses CRF 18 instead of default CRF 8 or source bitrate

Clone with metadata:

.\Clone-Video.ps1 -Source video.mp4 -Target output.mp4 -Metadata
# Copies all available JSON metadata from source to target

Reverse the video (audio stays forward):

.\Clone-Video.ps1 -Source video.mp4 -Target reversed.mp4 -Reverse
# Video frames are played from last to first; audio plays forward as in the source.

Reverse a trimmed segment, rescaled and re-timed:

.\Clone-Video.ps1 -Source video.mp4 -Target clip_rev.mp4 -StartFrame 100 -EndFrame 500 -Size 720p -FPS 30 -Reverse
# Selects frames 100-500, scales to 720p, retimes to 30 fps, then reverses the video.
# Audio is trimmed to match the same segment but plays forward.

Build video from a still image with audio (image-to-video mode):

.\Clone-Video.ps1 -Source photo.jpg -Target output.mp4 -Audio soundtrack.mp3
# Creates a video of the static image for the duration of soundtrack.mp3

Build image-to-video at a specific resolution:

.\Clone-Video.ps1 -Source photo.png -Target output.mp4 -Audio narration.wav -Size 720p

Build image-to-video with LTX-2 resolution:

.\Clone-Video.ps1 -Source photo.jpg -Target ltx.mp4 -Audio narration.wav -Size 1080p -LTX
# Picks the closest LTX-2 1080p bucket for the image's aspect ratio

Build video from PNG sequence (all defaults):

.\Clone-Video.ps1 -Source frames\ -Target output.mp4
# All PNG files in frames\ sorted alphabetically, 24 fps, CRF 8, no audio

Build from a frame range:

.\Clone-Video.ps1 -Source frames\ -Target clip.mp4 -StartFrame 10 -EndFrame 49
# Uses frame_0010.png through frame_0049.png (0-based indices into the sorted list)

Build reversed sequence:

.\Clone-Video.ps1 -Source frames\ -Target reversed.mp4 -Reverse
# PNG files sorted descending: last frame plays first

Build reversed range:

.\Clone-Video.ps1 -Source frames\ -Target clip_rev.mp4 -StartFrame 10 -EndFrame 49 -Reverse
# Frames 10-49 selected first, then reversed: frame_0049.png plays first

Build with custom FPS and CRF:

.\Clone-Video.ps1 -Source frames\ -Target output.mp4 -FPS 30 -CRF 18

Build with scaled output:

.\Clone-Video.ps1 -Source frames\ -Target output.mp4 -Size 720p
# Scales to 720p preserving aspect ratio of first PNG

Build with audio:

.\Clone-Video.ps1 -Source frames\ -Target output.mp4 -Audio soundtrack.mp3

Build with LTX-2 canonical resolution:

.\Clone-Video.ps1 -Source frames\ -Target ltx.mp4 -Size 720p -LTX
# Picks the closest LTX-2 720p bucket for the first PNG's aspect ratio

Build as WebM:

.\Clone-Video.ps1 -Source frames\ -Target output.webm -FPS 24
# VP9 + no audio; use -Audio to add a soundtrack

Using absolute paths:

.\Clone-Video.ps1 -Source "C:\Videos\input.mp4" -Target "D:\Output\result.mp4"

Convert a YouTube/Google DASH download (VP9 + Opus in WebM) to standard H.264 MP4:

.\Clone-Video.ps1 -Source download.webm -Target download.mp4
# Video is transcoded from VP9 to H.264 (libx264, yuv420p, slow preset)
# Audio is transcoded from Opus to AAC 192k

Convert an MKV source (e.g. yt-dlp default) to MP4:

.\Clone-Video.ps1 -Source movie.mkv -Target movie.mp4

Convert and rescale a VP9 WebM in one step:

.\Clone-Video.ps1 -Source download.webm -Target download.mp4 -Size 720p
# Single re-encode pass: VP9 -> H.264 at 720p

Transcode an MP4 to WebM (VP9 + Opus):

.\Clone-Video.ps1 -Source video.mp4 -Target video.webm
# Video transcoded H.264 -> VP9 (libvpx-vp9)
# Audio transcoded AAC -> Opus (libopus, 160k)

Stream-copy a YouTube WebM into MKV (no re-encoding):

.\Clone-Video.ps1 -Source download.webm -Target download.mkv
# MKV accepts VP9 and Opus directly; both streams are stream-copied.

Output containers

The output container is selected from the -Target file extension. Each container has its own codec profile, stream-copy list, and encoder settings.

Container Target video codec Target audio codec Source video codecs stream-copied Source audio codecs stream-copied
.mp4 (default) libx264 (yuv420p, preset slow) aac 192k h264 aac, mp3, ac3, eac3, alac
.webm libvpx-vp9 (yuv420p, deadline good, cpu-used 2, row-mt 1) libopus 160k vp8, vp9, av1 opus, vorbis
.mkv libx264 (yuv420p, preset slow) aac 192k h264, hevc, vp8, vp9, av1, mpeg4 aac, mp3, ac3, eac3, alac, flac, opus, vorbis

General rules

  • A source stream that's on the container's stream-copy list is copied without re-encoding when no trimming, resizing, frame rate change, or audio removal is requested.
  • A source stream that's not on the stream-copy list is transcoded to the container's target codec (e.g. VP9 → H.264 when the target is MP4, AAC → Opus when the target is WebM).
  • Trimming (-StartFrame/-EndFrame or -Frames) always forces an audio re-encode; in that case the audio is encoded with the target container's audio codec regardless of the source.
  • -Size always forces a video re-encode (scaling requires re-encoding). For standard named sizes (320p, 384p, 480p, 720p, 900p, 1080p/HD, 1440p/2K, 2160p/4K) the output preserves the source's aspect ratio — the reference height is the short-side target (landscape: computed width; portrait: computed height). Custom sizes (width:height) are used exactly.
  • -Crop replaces the scale filter with a centered crop filter for the size computed from -Size/-LTX; it forces a video re-encode like -Size.
  • -FPS always forces a video re-encode (frame rate conversion requires re-encoding).
  • -NoAudio always forces a video re-encode (removing the audio stream requires re-muxing the output file).
  • -Reverse always forces a video re-encode (the reverse ffmpeg filter is appended at the end of the video filter chain). Audio is never reversed, so it plays forward over the reversed video and follows the same stream-copy/transcode rules it would have without -Reverse.
  • -LTX only takes effect when -Size is HD, 1080p, 720p or 480p. Combining it with any other -Size value (or omitting -Size) is rejected with an explicit error so the user is never silently downgraded to the regular preset. The chosen bucket replaces the dimensions that -Size would normally produce; everything else (re-encode logic, frame range, FPS, CRF, audio handling) behaves exactly as with a plain -Size.
  • Video bitrate priority is the same for every container: -CRF (when specified) → source video bitrate (via -b:v) → -crf 14 fallback. For libvpx-vp9, -b:v 0 is automatically added alongside -crf so VP9's constant-quality mode is correctly enabled.
  • The -Audio replacement file is also probed; if its codec isn't on the target container's audio stream-copy list, it is transcoded to the container's audio codec.
  • If -Target has no extension (or an unrecognised one), .mp4 is appended and the MP4 profile is used. This preserves the script's prior single-container behaviour.
  • The script logs the chosen target container, the detected source codecs, and any external -Audio codec, indicating whether each will be transcoded before running ffmpeg.

Notes

  • WebM encoding is slow. libvpx-vp9 is much slower than libx264; the chosen cpu-used 2 -row-mt 1 settings strike a reasonable balance, but expect significantly longer rendering times than MP4.
  • MKV stream-copy is greedy. Because MKV accepts almost any codec, MKV output usually involves no re-encoding at all and is the fastest target for re-containerising downloads.

Existing Files

  • An existing -Target video is replaced without a prompt. ffmpeg runs with -y, so the old file is lost. Use -WhatIf first, or write to a new name.
  • The same applies to the image-to-video and PNG-sequence modes, and to a PNG that -Sequence would overwrite.
  • The folder that contains -Target must already exist. The script stops with an error instead of creating it.
  • The temporary concat list used for PNG sequences is deleted after the run.

Safe Execution

This script supports -WhatIf and -Confirm for safe execution:

  • -WhatIf: Simulate the operation without actually creating files. Shows what would be done.
  • -Confirm: Prompt for confirmation before each state-changing operation.

Simulate without making changes:

.\Clone-Video.ps1 -Source video.mp4 -Target output.mp4 -WhatIf

Prompt before creating files:

.\Clone-Video.ps1 -Source video.mp4 -Target output.mp4 -Sequence frame.png -Confirm