# How to Handle Video Trim and Speed Adjustments in Swift: A Complete Guide

> Learn to handle video trim and speed adjustments in Swift with Palmier Pro. Discover how to store clip data and enable real-time previews and accurate exports.

- Repository: [Palmier/palmier-pro](https://github.com/palmier-io/palmier-pro)
- Tags: how-to-guide
- Published: 2026-06-24

---

**Palmier Pro stores video trim and speed data in a Clip model that converts timeline deltas into source frame adjustments using the formula `Int((Double(deltaFrames) * speed).rounded())`, enabling real-time preview and accurate export.**

Handling video trim and speed adjustments in Swift requires a robust architecture that separates preview calculations from final model mutations. The `palmier-pro` repository demonstrates a production-ready approach using a **speed-aware Clip model**, pure-function editing engines, and SwiftUI drag gesture handling. This implementation ensures that when users trim clips or adjust playback speed, the underlying source frame calculations remain accurate for both real-time preview and final export.

## Understanding the Clip Model Architecture

The foundation of Palmier Pro's trimming system lies in the `Clip` struct defined in [`Sources/PalmierPro/Models/Timeline.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Models/Timeline.swift). This model holds both timeline positioning and trim-specific properties.

### The Clip Struct and Speed-Aware Properties

Each `Clip` maintains temporal data through several key fields:

- `startFrame`: The clip's position on the timeline
- `durationFrames`: How long the clip occupies in the timeline
- `trimStartFrame` and `trimEndFrame`: The source media boundaries
- `speed`: A `Double` representing playback velocity (1.0 = normal speed)

The model computes **source frame consumption** through a calculated property:

```swift
// In Sources/PalmierPro/Models/Timeline.swift
var sourceFramesConsumed: Int {
    Int((Double(durationFrames) * speed).rounded())
}

```

This property tells the renderer exactly how many source frames are needed to fill the clip's duration at the given speed, ensuring that trim operations account for time-stretched media.

## Real-Time Preview During Trim Operations

When users drag trim handles, Palmier Pro updates a **preview clip** immediately while deferring model mutations until the drag completes. This separation prevents unstable states during gesture recognition.

### Handling Drag Gestures in TimelineView

The `TimelineView` in [`Sources/PalmierPro/Timeline/TimelineView.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Timeline/TimelineView.swift) detects drag states through `DragState.TrimDrag`. During active drags, the view constructs a temporary preview clip that reflects the proposed trim:

```swift
// Inside TimelineView drag handling
guard let (drag, isLeft) = trimDrag,
      clip.id == drag.clipId || trimPartnerIds.contains(clip.id) else { return }

var preview = clip
let sourceDelta = Int((Double(drag.deltaFrames) * clip.speed).rounded())

```

### Calculating Source Frame Deltas

The critical calculation converts timeline frame deltas into source frame adjustments using the clip's speed factor:

```swift
// Left-hand trim (adjusting start)
if isLeft {
    preview.startFrame = clip.startFrame + drag.deltaFrames
    preview.trimStartFrame = clip.trimStartFrame + sourceDelta
    preview.durationFrames = clip.durationFrames - drag.deltaFrames
} else {
    // Right-hand trim (adjusting end)
    preview.durationFrames = clip.durationFrames + drag.deltaFrames
    preview.trimEndFrame = clip.trimEndFrame - sourceDelta
}

```

The `ClipRenderer` then draws using this preview data, allowing users to see exact source frames before committing changes.

## Committing Trim Changes with OverwriteEngine

Once the user releases the drag gesture, `EditorViewModel` invokes the `OverwriteEngine` to compute final actions. This engine operates as a **pure function** that determines whether to trim, split, or remove clips based on the new region boundaries.

### Pure Functions for Trim, Split, and Remove Actions

The `OverwriteEngine.computeOverwrite` method in [`Sources/PalmierPro/Editor/OverwriteEngine.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Editor/OverwriteEngine.swift) analyzes clip overlaps and returns specific actions:

- `.trimStart` or `.trimEnd`: For adjustments within clip boundaries
- `.split`: When the trim handle crosses into adjacent clips
- `.remove`: For clips completely covered by the operation region

### Speed-Aware Split Logic

When splitting clips, the engine multiplies the region delta by the clip's speed to calculate the correct source frame boundary:

```swift
// In Sources/PalmierPro/Editor/OverwriteEngine.swift
if cs < regionStart && ce > regionEnd {
    // Clip spans the region – split required
    let rightTrimStart = clip.trimStartFrame +
        Int((Double(regionEnd - cs) * clip.speed).rounded())
    
    actions.append(.split(
        clipId: clip.id,
        leftDuration: regionStart - cs,
        rightId: UUID().uuidString,
        rightStartFrame: regionEnd,
        rightTrimStart: rightTrimStart,
        rightDuration: ce - regionEnd
    ))
}

```

The `EditorViewModel+ClipMutations` extension then applies these actions to the actual model objects, handling the creation of new clip IDs and boundary adjustments.

## Exporting Speed and Trim Data

Palmier Pro persists speed adjustments during export through time-remap filters, ensuring compatibility with external editing software that cannot infer speed from duration alone.

### XML Export with Time-Remap Filters

The `XMLExporter` in [`Sources/PalmierPro/Export/XMLExporter.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Export/XMLExporter.swift) converts the internal `speed` property into industry-standard time-remap values:

```swift
// Speed expressed as percentage for Premiere/FCXML compatibility
let timeRemapSpeed = clip.speed * 100 // e.g., 0.5 becomes 50%

```

This approach ensures that when projects import into professional editing software, the trim points and speed adjustments remain frame-accurate.

## Summary

- **Clip Model**: Stores `trimStartFrame`, `trimEndFrame`, and `speed`, with `sourceFramesConsumed` calculating required source frames using `Int((Double(durationFrames) * speed).rounded())`.
- **Preview System**: `TimelineView` creates temporary preview clips during drag gestures, applying speed-adjusted deltas before rendering.
- **Commit Logic**: `OverwriteEngine` uses pure functions to determine trim, split, or remove actions, with speed-aware calculations for split boundaries.
- **Split Implementation**: `EditorViewModel+ClipMutations` converts timeline offsets to source frames using the speed multiplier when dividing clips.
- **Export Compatibility**: `XMLExporter` expresses speed as time-remap percentages (speed × 100) for external editor compatibility.

## Frequently Asked Questions

### How does Palmier Pro handle speed adjustments when trimming video clips?

Palmier Pro multiplies all timeline deltas by the clip's `speed` property to calculate source frame adjustments. When trimming in [`TimelineView.swift`](https://github.com/palmier-io/palmier-pro/blob/main/TimelineView.swift), the code computes `sourceDelta = Int((Double(drag.deltaFrames) * clip.speed).rounded())`, ensuring that the trim boundaries align with the correct source media frames even when the clip plays at 2× or 0.5× speed.

### What is the purpose of the OverwriteEngine in Palmier Pro?

The `OverwriteEngine` in [`Sources/PalmierPro/Editor/OverwriteEngine.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Editor/OverwriteEngine.swift) functions as a pure-function decision engine that determines how to modify the timeline when clips overlap or undergo trim operations. It returns specific actions like `.trimStart`, `.trimEnd`, `.split`, or `.remove` based on region calculations, keeping the business logic testable and separate from the UI layer.

### How are trim operations converted to source frame adjustments?

The conversion occurs through a consistent formula applied across multiple files: `Int((Double(timelineDelta) * speed).rounded())`. This calculation appears in [`TimelineView.swift`](https://github.com/palmier-io/palmier-pro/blob/main/TimelineView.swift) for preview generation, [`OverwriteEngine.swift`](https://github.com/palmier-io/palmier-pro/blob/main/OverwriteEngine.swift) for split operations, and `EditorViewModel+ClipMutations.swift` for final clip mutations, ensuring that speed adjustments always factor into source frame boundaries.

### How does Palmier Pro export speed data to external editors?

The `XMLExporter` translates the internal `speed` property (stored as a Double such as 0.5 or 2.0) into time-remap filter percentages by multiplying by 100. This produces values like 50% for half-speed or 200% for double-speed, which professional editing software like Adobe Premiere can interpret correctly when importing the exported XML files.