How ExportService Handles Different Export Formats in Palmier Pro
The ExportService class centralizes export logic in Palmier Pro by routing XML exports through a dedicated XMLExporter while processing video formats (H.264, H.265, and ProRes) through AVAssetExportSession with hardware-accelerated presets mapped via exportPresetName(format:resolution:).
The Palmier Pro repository manages all export operations within Sources/PalmierPro/Export/ExportService.swift. This service accepts an ExportFormat enum—defining h264, h265, prores, and xml cases—alongside a target resolution and output URL, then executes format-specific branching logic to ensure optimal file generation.
Export Format Architecture
The ExportFormat enum defines both the file extension and the AVFoundation type required for each supported format. According to the source code at lines 4-21, each case provides a utType property that maps to AVFileType for video exports, while the xml case returns nil to trigger the specialized XML pipeline.
enum ExportFormat {
case h264, h265, prores, xml
var fileExtension: String { … }
var utType: AVFileType? { … }
}
XML Export: The Fast Path
When the service receives format == .xml, it bypasses AVFoundation entirely. At lines 90-99, the export() method immediately delegates to XMLExporter.export(...), which generates a Final Cut Pro 7 compatible XMEML-4 file using a pure-Swift XML builder.
This path sets progress = 1.0 instantly upon completion, avoiding the overhead of video composition and encoding. The ExportResolution parameter is ignored for XML exports, as the XML contains only metadata and edit decision lists rather than rendered pixels.
Video Export: AVAssetExportSession Pipeline
For H.264, H.265, and ProRes formats, ExportService enters a comprehensive export pipeline that leverages AVAssetExportSession. The process begins at lines 102-107 by setting isExporting = true and resetting progress tracking.
The service constructs the export session through makeExportSession(...), which builds an AVComposition via CompositionBuilder.build and selects an appropriate preset based on the format and resolution combination.
Format-Specific Preset Mapping
The private method exportPresetName(format:resolution:) (lines 64-87) maps each format and resolution to a specific AVFoundation preset string:
- H.264: Uses
AVAssetExportPreset1280x720for 720p,AVAssetExportPreset1920x1080for 1080p,AVAssetExportPreset3840x2160for 4K, andAVAssetExportPresetHighestQualityfor 2K or native resolutions. - H.265 (HEVC): Maps to
AVAssetExportPresetHEVCHighestQualityfor 720p and 2K/native,AVAssetExportPresetHEVC1920x1080for 1080p, andAVAssetExportPresetHEVC3840x2160for 4K. - ProRes: Applies
AVAssetExportPresetAppleProRes422LPCMuniformly across all resolutions, ensuring professional-grade quality with LPCM audio.
File Preparation and Progress Monitoring
Before writing, the service removes any existing file at the output URL (lines 25-27) because AVAssetExportSession cannot overwrite existing files. It then initiates a lightweight Task that polls session.progress every 200 milliseconds to update the observable progress property (lines 29-35).
The actual export executes at lines 38-40 using session.export(to:outputURL, as:fileType), where fileType derives from format.utType.
Error Handling and Cancellation
The service distinguishes between user cancellation and system failures. When catching errors at lines 45-60, it checks for NSUserCancelledError to handle cancellation gracefully, while other errors trigger Log.export.error logging and storage in self.error.
Two specific error types can bubble up during initialization:
ExportError.invalidFormatoccurs if a format'sutTypeis unexpectedlynil.ExportError.unsupportedPresettriggers when the system cannot create the requested export preset.
Code Examples
Exporting to H.264 at 1080p
let service = ExportService()
await service.export(
timeline: myTimeline,
resolver: myResolver,
format: .h264,
resolution: .r1080p,
outputURL: URL(fileURLWithPath: "/Users/me/Movies/project.mp4")
)
// Progress observable via service.progress
This invocation creates an AVAssetExportSession configured with AVAssetExportPreset1920x1080.
Generating Final Cut Pro XML
let service = ExportService()
await service.export(
timeline: myTimeline,
resolver: myResolver,
format: .xml,
resolution: .native, // Ignored for XML
outputURL: URL(fileURLWithPath: "/Users/me/Exports/project.xml")
)
The service detects .xml and routes to XMLExporter.export, completing instantly with progress set to 1.0.
Handling Export Errors
await service.export(…)
if let error = service.error {
print("Export failed: \(error)")
}
Errors are captured in service.error and logged through the Log.export channel.
Summary
- Branching Logic:
ExportServiceseparates XML exports (handled byXMLExporter.swift) from video exports (usingAVAssetExportSession). - Preset Mapping: Format and resolution combinations map to specific AVFoundation presets via
exportPresetName(format:resolution:). - Progress Tracking: A 200ms polling Task updates the
progressproperty during video exports. - Error Safety: The service validates
utTypebefore export and handles file-overwrite constraints by deleting existing files first. - Hardware Acceleration: Video formats leverage macOS 26 hardware encoders through standardized AVFoundation presets.
Frequently Asked Questions
What is the difference between XML and video export paths in ExportService?
Video exports require AVAssetExportSession initialization, composition building, and asynchronous encoding with progress monitoring. XML exports follow a synchronous path through XMLExporter.export, which generates a Final Cut Pro 7 compatible XMEML file without creating any AVFoundation sessions, resulting in immediate completion.
How does ExportService handle unsupported format combinations?
If the ExportFormat enum produces a nil utType (which only occurs for XML, handled separately), the service throws ExportError.invalidFormat. If the system cannot create an export preset for the requested format and resolution, ExportError.unsupportedPreset bubbles up from makeExportSession.
Why does ExportService delete the output file before exporting?
AVAssetExportSession throws an error when attempting to write to a path that already exists. At lines 25-27, ExportService preemptively removes existing files using FileManager.default.removeItem(at:outputURL) to ensure the export operation can proceed without manual cleanup.
Does ProRes export support different quality presets per resolution?
No. According to the implementation in exportPresetName(format:resolution:), ProRes exports use AVAssetExportPresetAppleProRes422LPCM for all resolutions. This Apple preset provides consistent 422 quality with LPCM audio regardless of the target resolution parameter.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →