# How to Use Frame Ranges to Selectively Convert Video Portions in FaceSwap

> Process specific video portions with FaceSwap using the frame ranges flag. Save time by converting only necessary segments of your video. Learn how to use --frame-ranges now.

- Repository: [deepfakes/faceswap](https://github.com/deepfakes/faceswap)
- Tags: how-to-guide
- Published: 2026-03-06

---

**Use the `--frame-ranges` flag in FaceSwap's convert command to process only specific frame segments, drastically reducing processing time when you need to convert just a portion of a video.**

FaceSwap's conversion pipeline supports **frame ranges** to let you selectively process specific portions of a video without converting the entire sequence. This feature is particularly valuable when working with long videos where you only need to apply face swapping to specific segments, saving significant processing time and computational resources.

## Understanding Frame Ranges in FaceSwap

Frame ranges allow you to define one or more contiguous segments of frames that FaceSwap will process, while skipping everything else. When you specify ranges, the tool creates a whitelist of frame indices and filters the input stream accordingly.

### How Frame Ranges Work Internally

The implementation follows a five-stage pipeline inside the `DiskIO` class in [`scripts/convert.py`](https://github.com/deepfakes/faceswap/blob/main/scripts/convert.py):

1. **Argument parsing** – The CLI captures your range strings in `args.frame_ranges` as a list of strings (e.g., `["100-500", "800-1200"]`).

2. **Range calculation** – `DiskIO._get_frame_ranges()` (lines 2100-2146) parses these strings into tuples of `(start, end)` integers. The method automatically clips these values to the actual frame boundaries detected in your source (`minframe` to `maxframe`).

3. **Skip-frame logic** – As each input file loads, `_check_skipframe()` (lines 2850-2856) extracts the frame number from the filename using regex. If the number falls outside your defined ranges, the frame is either discarded or passed through unchanged (depending on `--keep-unchanged`).

4. **Total-frame count** – The `_total_count` property (lines 2720-2729) calculates the exact number of frames to process. When ranges are active and `--keep-unchanged` is not set, this equals the sum of all range lengths; otherwise, it reports the full input count.

5. **Writer initialization** – `_get_writer()` (lines 1990-2008) passes the frame range list to the output writer (FFmpeg or GIF). This ensures the final video is stitched correctly without gaps or timing errors.

## Using Frame Ranges from the Command Line

FaceSwap's convert command accepts frame ranges through the `--frame-ranges` parameter followed by space-separated start-end pairs.

### Converting Specific Frame Segments

To process only frames 100 through 500 and frames 800 through 1200:

```bash
python faceswap.py convert \
    -i /path/to/input_frames \
    -o /path/to/output_frames \
    -m /path/to/model \
    --frame-ranges 100-500 800-1200

```

- Frames outside these ranges are skipped entirely and will not appear in the output directory.
- The ranges are inclusive (both start and end frames are processed).

### Preserving Unchanged Frames Outside Ranges

Add the `--keep-unchanged` flag to copy unprocessed frames to the output while only converting frames within your specified ranges:

```bash
python faceswap.py convert \
    -i video_frames \
    -o swapped_frames \
    -m model_dir \
    --frame-ranges 300-600 \
    --keep-unchanged

```

- Frames 300-600 undergo face swapping.
- All other frames are copied verbatim to maintain video timing and duration.
- This is essential when you need to re-encode the full video but only want to process specific segments.

### Working with Video Files Directly

FaceSwap treats video files as virtual frame sources. The same `--frame-ranges` syntax applies:

```bash
python faceswap.py convert \
    -i source_video.mp4 \
    -o result_video.mp4 \
    -m model_dir \
    --frame-ranges 0-2500

```

- When using video input, `minframe` is forced to **1** and `maxframe` equals the total frame count detected by `ImagesLoader` (defined in [`scripts/fsmedia.py`](https://github.com/deepfakes/faceswap/blob/main/scripts/fsmedia.py)).
- Out-of-bounds range values are automatically clipped to valid frame numbers, preventing errors.

## Programmatic Access to Frame Ranges

If you are embedding FaceSwap in a Python application, you can trigger frame-range filtering programmatically:

```python
from scripts.convert import Convert
import argparse

# Set up the argument parser with FaceSwap's expected parameters

parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input', required=True, help='Input directory or video')
parser.add_argument('-o', '--output', required=True, help='Output directory or video')
parser.add_argument('-m', '--model', required=True, help='Model directory')
parser.add_argument('--frame-ranges', nargs='*', default=None,
                    help='Space-separated start-end pairs, e.g., 10-200 400-600')
parser.add_argument('--keep-unchanged', action='store_true',
                    help='Copy unprocessed frames to output')

# Parse example arguments

args = parser.parse_args([
    '-i', 'frames/', 
    '-o', 'out/', 
    '-m', 'model/', 
    '--frame-ranges', '10-200', '400-600'
])

# Execute the conversion with frame range filtering

Convert(args).process()

```

The `Convert` class instantiates `DiskIO`, which internally calls `_get_frame_ranges()` to parse your range strings and enforce the filtering logic.

## Key Implementation Details

The frame range functionality is distributed across several core files:

| File | Role | Relevant Sections |
|------|------|-------------------|
| [`scripts/convert.py`](https://github.com/deepfakes/faceswap/blob/main/scripts/convert.py) | Core conversion driver that parses CLI arguments, creates `DiskIO`, and manages predictor and writer threads. | `Convert.__init__`, `DiskIO._get_frame_ranges` (lines 2100-2146), `DiskIO._check_skipframe` (lines 2850-2856), `DiskIO._total_count` (lines 2720-2729), `DiskIO._get_writer` (lines 1990-2008) |
| [`scripts/fsmedia.py`](https://github.com/deepfakes/faceswap/blob/main/scripts/fsmedia.py) | Handles image and video loading via `ImagesLoader`; provides `is_video` flag used to determine `minframe` and `maxframe` boundaries. | `ImagesLoader` class |
| [`plugins/convert/writer/ffmpeg.py`](https://github.com/deepfakes/faceswap/blob/main/plugins/convert/writer/ffmpeg.py) | FFmpeg writer plugin that receives `frame_ranges` to ensure correct video stitching without gaps. | `FFmpegWriter.__init__` |
| [`plugins/convert/writer/gif.py`](https://github.com/deepfakes/faceswap/blob/main/plugins/convert/writer/gif.py) | GIF writer plugin that similarly accepts frame range metadata for proper animation timing. | `GifWriter.__init__` |
| [`lib/utils.py`](https://github.com/deepfakes/faceswap/blob/main/lib/utils.py) | Utility functions for folder handling and error reporting used throughout the pipeline. | `get_folder`, `FaceswapError` |

## Summary

- **Frame ranges** allow you to process specific video segments using the `--frame-ranges` flag followed by start-end pairs (e.g., `100-500 800-1200`).
- Internally, `DiskIO._get_frame_ranges()` in [`scripts/convert.py`](https://github.com/deepfakes/faceswap/blob/main/scripts/convert.py) parses and validates these ranges against actual frame boundaries.
- Use `--keep-unchanged` to copy unprocessed frames to the output, preserving video timing while only converting selected segments.
- The feature works with both extracted frame sequences and direct video file inputs, with automatic clipping to valid frame numbers.
- Writer plugins (FFmpeg, GIF) receive range metadata to ensure seamless video reconstruction without gaps.

## Frequently Asked Questions

### What happens if I specify frame ranges outside my video's duration?

FaceSwap automatically clips your requested ranges to the actual frame boundaries detected in the source. In `DiskIO._get_frame_ranges()` (lines 2100-2146), the code compares your start-end values against `minframe` and `maxframe`, adjusting any out-of-bounds numbers to valid indices. This prevents errors and ensures you process every available frame within your specified ranges.

### Can I use multiple frame ranges in a single command?

Yes. The `--frame-ranges` argument accepts multiple space-separated start-end pairs. For example, `--frame-ranges 100-500 800-1200 1500-2000` processes three distinct segments while skipping everything else. Internally, these are stored as a list of tuples and checked sequentially during the `_check_skipframe` validation (lines 2850-2856).

### Does using frame ranges affect the output video timing?

Without the `--keep-unchanged` flag, the output contains only the processed frames, resulting in a shorter video that jumps between your selected segments. When you include `--keep-unchanged`, frames outside the ranges are copied verbatim to the output, preserving the original timing and duration while only the specified ranges undergo face conversion. The writer plugins (FFmpeg/GIF) use the `frame_ranges` metadata to ensure proper frame sequencing in both scenarios.

### How does `--keep-unchanged` differ from omitting the flag?

Omitting `--keep-unchanged` causes FaceSwap to discard any frames outside your specified ranges, producing an output that contains only the converted segments. Adding `--keep-unchanged` triggers the skip-frame logic to copy unprocessed frames directly to the output directory (or include them in the video writer), ensuring you retain the full frame sequence with only your selected ranges modified. This is controlled in the `_check_skipframe` method (lines 2850-2856) and affects the `_total_count` calculation (lines 2720-2729).