# How Palmier Pro's Export Pipeline Handles Timeline Composition and Format Conversion

> Learn how Palmier Pro's export pipeline manages timeline composition and format conversion, creating AVMutableCompositions and exporting to various formats like FCP and Premiere.

- Repository: [Palmier/palmier-pro](https://github.com/palmier-io/palmier-pro)
- Tags: internals
- Published: 2026-06-29

---

**Palmier Pro's export pipeline operates in two distinct phases: first composing raw media tracks into an `AVMutableComposition` with synchronized audio mixes and custom video compositions, then converting the assembled asset to the target format using either `AVAssetExportSession` for video codecs or `XMLExporter` for Final Cut Pro and Premiere interchange formats.**

Palmier Pro is an open-source Swift framework for building professional video editing applications, maintained in the `palmier-io/palmier-pro` repository. Understanding how its export pipeline handles timeline composition and format conversion is essential for developers integrating custom export workflows or troubleshooting media processing bottlenecks. The architecture cleanly separates timeline assembly from encoding concerns, enabling support for everything from H.264 MP4s to FCPXML interchange files.

## Export Entry Point and Format Routing

The export process begins in [`Sources/PalmierPro/Export/ExportService.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Export/ExportService.swift), where the `export` method acts as a router between video encoding and XML serialization paths.

```swift
// Sources/PalmierPro/Export/ExportService.swift
func export(
    timeline: Timeline,
    resolver: MediaResolver,
    format: ExportFormat,
    resolution: ExportResolution,
    fcpxmlVersion: FCPXMLVersion = .default,
    missingMediaRefs: Set<String> = [],
    outputURL: URL,
    acquireSlot: Bool = true
) async {
    // XML-only formats return early
    if format == .xml || format == .fcpxml {
        try await XMLExporter.export(timeline: timeline,
                                     resolver: resolver,
                                     outputURL: outputURL)
        return
    }
    // … otherwise build a full AVFoundation composition
    let prepared = try await makeExportSession(
        timeline: timeline, resolver: resolver,
        format: format, resolution: resolution,
        missingMediaRefs: missingMediaRefs)
    // Export logic continues...
}

```

According to the Palmier Pro source code at lines 32-71, the method immediately shortcuts to `XMLExporter` for `.xml` or `.fcpxml` formats. For video formats like `.h264`, `.h265`, or `.prores`, it proceeds to `makeExportSession`, which orchestrates the timeline composition phase before handing off to `AVAssetExportSession`.

## Timeline Composition Phase

The **timeline composition** phase transforms the abstract timeline model into a concrete `AVMutableComposition` ready for encoding. This logic lives in [`Sources/PalmierPro/Preview/CompositionBuilder.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Preview/CompositionBuilder.swift).

### Source Asset Loading and Validation

The `CompositionBuilder.build` method (lines 34-124) first validates timeline dimensions and frame rate, then iterates through each track to assemble media. For each clip, it resolves the source URL and handles type-specific processing:

- **Images** are converted to video via `ImageVideoGenerator`
- **Lottie animations** are rendered via `LottieVideoGenerator`
- **Video files** are optionally processed through `AlphaVideoNormalizer` for premultiplied alpha channels

```swift
// Sources/PalmierPro/Preview/CompositionBuilder.swift
static func build(
    timeline: Timeline,
    resolveURL: @Sendable (String) -> URL?,
    resolveSourceSize: @Sendable (String) -> CGSize? = { _ in nil },
    missingMediaRefs: Set<String> = [],
    renderSize: CGSize
) async throws -> CompositionResult {
    // 1️⃣ Validate timeline dimensions & FPS
    // 2️⃣ Iterate each track, filter out text clips (handled later)
    // 3️⃣ Load source assets and insert into composition tracks...
}

```

### Track Assembly and Gap Handling

For each valid media reference, the pipeline inserts the clip into the composition track using `insertClip` (lines 104-148). This method handles:

- **Gap insertion** for empty timeline regions
- **Speed scaling** via `scaleTimeRange` for time-stretched clips
- **Track mapping** that records natural sizes and transforms for later visual processing

The builder also guarantees a valid video output by inserting an opaque black background track via `insertBlackBackground` (lines 52-78), ensuring the composition never results in an empty video track even when processing audio-only sources.

### Visual Composition and Audio Mixing

After track assembly, `buildVisuals` (lines 80-124) constructs the final deliverables:

- **`AVMutableAudioMix`** with precise volume ramps and mute handling per clip
- **`AVVideoComposition`** utilizing a `CustomVideoCompositor` for transform, opacity, and crop operations
- **Layer plans** that map clip boundaries to `CompositorInstruction` objects

The `compositorInstructions` method (lines 36-98) splits the timeline at every clip boundary, generating instructions that the `CustomVideoCompositor` uses to blend layers correctly.

## Format Conversion Phase

Once composition is complete, the pipeline enters the **format conversion** phase, diverging based on the selected `ExportFormat`.

### Video Export via AVAssetExportSession

For H.264, H.265, and ProRes formats, `ExportService` creates an `AVAssetExportSession` configured via `exportPresetName(format:resolution:)` at lines 68-90:

```swift
private func exportPresetName(format: ExportFormat, resolution: ExportResolution) -> String {
    switch format {
    case .h264:
        switch resolution {
        case .r720p: return AVAssetExportPreset1280x720
        case .r1080p: return AVAssetExportPreset1920x1080
        case .r4k: return AVAssetExportPreset3840x2160
        case .r1440p, .matchTimeline: return AVAssetExportPresetHighestQuality
        }
    case .prores:
        return AVAssetExportPresetAppleProRes422LPCM
    // ...
    }
}

```

The session receives the composed asset, applies the selected preset, and exports to the specified `outputURL` with the appropriate file extension (`.mp4` for H.264/H.265, `.mov` for ProRes).

### XML Export for Professional Workflows

When exporting to Final Cut Pro or Premiere formats, the pipeline bypasses AVFoundation composition entirely. As implemented in [`Sources/PalmierPro/Export/ExportService.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Export/ExportService.swift) at lines 41-46, the method invokes `XMLExporter.export` (located in [`Sources/PalmierPro/Export/XMLExporter.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Export/XMLExporter.swift)) to serialize the timeline structure directly to `.xml` (XMEML) or `.fcpxml` format.

## Text Overlay Rendering

Text clips require special handling because they are **not** inserted into the `AVMutableComposition` tracks. During export, `TextLayerController.buildForExport` creates a Core Animation layer hierarchy based on the timeline's text clips. The `export` method then wraps this in an `AVVideoCompositionCoreAnimationTool` (lines 105-112 in [`ExportService.swift`](https://github.com/palmier-io/palmier-pro/blob/main/ExportService.swift)):

```swift
let (parent, videoLayer) = TextLayerController.buildForExport(
    timeline: timeline, fps: timeline.fps, renderSize: renderSize)
let animationTool = AVVideoCompositionCoreAnimationTool(
    postProcessingAsVideoLayer: videoLayer, in: parent)
session.videoComposition = ExportService.addingAnimationTool(animationTool,
                                                            to: prepared.session.videoComposition)

```

This ensures text overlays render correctly into the final video file even though they exist outside the standard media track structure.

## Error Handling and Media Status

The pipeline tracks two specific failure categories during composition:

- **`offlineMediaRefs`** – Clips whose media reference could not be resolved (missing files)
- **`unprocessableMediaRefs`** – Clips that resolved but failed processing (corrupt files or unsupported codecs)

Both sets are collected in `CompositionBuilder` (lines 50-53) and bubble up through `ExportService`, allowing the UI to report specific problematic assets without crashing the entire export operation.

## Summary

Palmier Pro's export architecture demonstrates a clean separation of concerns across distinct phases:

- **Composition** occurs in `CompositionBuilder`, generating `AVMutableComposition`, audio mixes, and video compositions with custom compositor instructions
- **Format routing** happens in `ExportService`, dispatching XML formats to `XMLExporter` while video formats proceed through `AVAssetExportSession`
- **Text rendering** leverages `AVVideoCompositionCoreAnimationTool` to bake text overlays into the final output
- **Error resilience** tracks offline and unprocessable media references throughout the pipeline for detailed user feedback

## Frequently Asked Questions

### What is the difference between video export and XML export in Palmier Pro?

Video export (H.264, H.265, ProRes) requires building a full `AVMutableComposition` with audio mixes and video compositions, then encoding via `AVAssetExportSession`. XML export (`.xml` or `.fcpxml`) bypasses composition entirely, serializing the timeline structure directly through `XMLExporter` for interchange with Final Cut Pro or Premiere.

### How does Palmier Pro handle text overlays during export?

Text clips are rendered using `AVVideoCompositionCoreAnimationTool`. The `TextLayerController.buildForExport` method creates a Core Animation layer hierarchy that is attached to the `AVAssetExportSession` as a post-processing video layer, ensuring text renders correctly without being part of the composition tracks.

### What happens when media files are missing during export?

The pipeline collects missing references in the `offlineMediaRefs` set during the `CompositionBuilder` phase. These are bubbled up through `ExportService`, allowing the application to report specific missing files to the user while potentially continuing with other valid media.

### Why does the export pipeline insert a black background track?

The `insertBlackBackground` method (lines 52-78 in [`CompositionBuilder.swift`](https://github.com/palmier-io/palmier-pro/blob/main/CompositionBuilder.swift)) guarantees a non-empty video track even when all source clips are audio-only. This prevents `AVAssetExportSession` from failing due to missing video tracks and ensures consistent output across all timeline configurations.