Using HEVC video with alpha
At a glance
| Item | Summary |
|---|---|
| Purpose | Demonstrate HEVC-alpha playback, SceneKit-to-AVAssetWriter generation, and several AVAssetExportSession conversions. |
| App architecture | One workspace contains three independent targets: playback app, asset-writing app, and command-line export examples. |
| Main patterns | Linear media pipelines, framework delegate, callback completion, per-target composition. |
| Project style | Separate project folders and resources for Playback, AssetWriting, and Exports; intentionally little shared app infrastructure. |
Project structure
HEVC-Videos-With-Alpha.xcworkspace
├── HEVC-Videos-With-Alpha-Playback/
│ ├── ViewController.swift
│ └── Resources/
├── HEVC-Videos-With-Alpha-AssetWriting/
│ ├── AppDelegate.swift
│ └── Resources/
└── HEVC-Videos-With-Alpha-Exports/
├── main.swift
└── Resources/
These are complementary demonstrations, not layers of a single application. Architecture and ownership therefore need to be read per target.
Overall architecture
flowchart LR
Workspace["Sample workspace"] --> Playback["Playback target"]
Workspace --> Writing["Asset-writing target"]
Workspace --> Exports["CLI export target"]
Playback --> Sprite["SKVideoNode + AVPlayer"]
Writing --> Render["SceneKit / Metal frames"]
Render --> Writer["AVAssetWriter HEVC alpha"]
Exports --> Session["AVAssetExportSession"]
Exports --> Filter["Core Image chroma key"]
Reference code
HEVC-Videos-With-Alpha-Playback/HEVC-Videos-With-Alpha-Playback/ViewController.swift:31 — the playback target is deliberately direct (abridged).
guard let alphaMovieURL = Bundle.main.url(
forResource: "puppets_with_alpha_hevc", withExtension: "mov"
) else { return }
videoPlayer = AVPlayer(url: alphaMovieURL)
let video = SKVideoNode(avPlayer: videoPlayer)
scene.addChild(video)
videoPlayer.play()The playback app composes the alpha video over a SpriteKit scene. It does not share services with the writing or export targets.
Ownership and state
classDiagram
PlaybackViewController *-- AVPlayer
PlaybackViewController *-- SKVideoNode : creates
AssetWritingAppDelegate *-- SCNRenderer
AssetWritingAppDelegate *-- CVMetalTextureCache
AssetWritingAppDelegate --> AVAssetWriter : per export
ExportMain --> AVAssetExportSession : per operation
Ownership evidence
HEVC-Videos-With-Alpha-AssetWriting/HEVC-Videos-With-Alpha-AssetWriting/AppDelegate.swift:31 — the writing app owns long-lived renderer resources and creates writer resources per export (abridged).
let renderer = SCNRenderer(device: nil, options: nil)
var lampMaterials: SCNNode!
var metalTextureCache: CVMetalTextureCache!
var frameCounter = 0
func makeAssetWriter(outputURL: URL, fileType: AVFileType) throws
-> (assetWriter: AVAssetWriter,
writerInput: AVAssetWriterInput,
adaptor: AVAssetWriterInputPixelBufferAdaptor) {
// ... creates one writer graph for an export
}| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
Playback ViewController |
Player and created video node | Controller-lifetime playback | Controller starts playback; SpriteKit scene owns the added node. |
Asset-writing AppDelegate |
Renderer, texture cache, frame counter | App-lifetime render resources | App delegate renders and advances frames. |
| One export invocation | Writer/input/adaptor tuple | Local closure-captured graph | Writing callback appends buffers and finishes it. |
| CLI top-level workflow | Export sessions and dispatch group | Local operation lifetime | Each helper configures its own session; callbacks report status. |
Class and protocol design
classDiagram
class NSApplicationDelegate {
<<protocol>>
}
class SCNSceneRendererDelegate {
<<protocol>>
}
AssetWritingAppDelegate ..|> NSApplicationDelegate
AssetWritingAppDelegate ..|> SCNSceneRendererDelegate
PlaybackViewController --|> NSViewController
Reference code
HEVC-Videos-With-Alpha-AssetWriting/HEVC-Videos-With-Alpha-AssetWriting/AppDelegate.swift:21 — the writing app uses framework protocols, not an app-defined abstraction (abridged).
@NSApplicationMain
class AppDelegate: NSObject,
NSApplicationDelegate,
SCNSceneRendererDelegate {
// ...
func renderer(_ renderer: SCNSceneRenderer,
updateAtTime time: TimeInterval) {
let angle = CGFloat(Double.pi * 2 * time / ExportSettings.duration.seconds)
lampMaterials.eulerAngles = SCNVector3(x: 0, y: angle, z: 0)
}
}No custom protocol-oriented layer exists. The sample relies on AVFoundation, SceneKit, SpriteKit, and AppKit protocol/class contracts directly.
Access control
| Symbol | Access | Verified effect | Likely rationale |
|---|---|---|---|
| Most target types and properties | internal default | Visible throughout their app/CLI target. | Educational sample favors direct examples over encapsulated subsystems. |
ExportSettings values |
internal static let |
Shared constants within the asset-writing target. | Groups fixed output configuration without an instance. |
AVAssetExportSession.Status.description |
public |
Satisfies a publicly visible description witness on the extended framework type. | Makes status printable; it is not an app service API. |
| Export helper locals | lexical local scope | Sessions and compositions exist only inside one operation. | Operation lifetime itself provides encapsulation. |
HEVC-Videos-With-Alpha-Exports/HEVC-Videos-With-Alpha-Exports/main.swift:14 (abridged):
extension AVAssetExportSession.Status: CustomStringConvertible {
public var description: String {
switch self {
case .completed: return "completed"
case .failed: return "failed"
// ...
}
}
}Logic ownership and placement
| Logic | Owning type or file | Placement rationale |
|---|---|---|
| Alpha playback composition | Playback ViewController |
Tiny target needs one scene/player integration point. |
| Scene frame rendering | AssetWriting AppDelegate |
Owns renderer delegate and app UI action. |
| Writer configuration/append loop | AssetWriting AppDelegate methods |
Keeps pixel buffer pool, timestamps, and finish lifecycle together. |
| Preset export/fallback/chroma-key recipes | Exports main.swift helpers |
Each top-level function is one standalone conversion recipe. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| Media pipeline | HEVC-Videos-With-Alpha-AssetWriting/HEVC-Videos-With-Alpha-AssetWriting/AppDelegate.swift:164 |
Renders frames, appends pixel buffers, then finishes one writer. |
| Framework delegate | HEVC-Videos-With-Alpha-AssetWriting/HEVC-Videos-With-Alpha-AssetWriting/AppDelegate.swift:120 |
SceneKit requests per-frame animation updates. |
| Callback completion | HEVC-Videos-With-Alpha-Exports/HEVC-Videos-With-Alpha-Exports/main.swift:29 |
Export helpers return asynchronous status without shared state. |
| Multi-target examples | HEVC-Videos-With-Alpha.xcworkspace/contents.xcworkspacedata:1 |
Keeps playback, generation, and conversion independently runnable. |
Naming conventions
- Target names describe use cases:
Playback,AssetWriting, andExports. - Functions name source/destination transformations (
exportToHEVCWithAlpha...,exportFromHEVCWithAlphaToAVC...). - Fixed encoding dimensions/timing live under
ExportSettings. - Framework-generic names (
AppDelegate,ViewController) reflect the very small target scope.
Architecture takeaways
- Read this archive as three small pipelines, not one app architecture.
- Keep writer/input/adaptor lifetime together for a single export operation.
- Local helper functions can be sufficient boundaries for a recipe-style command-line target.
- App-defined protocol abstraction is absent; framework protocols are the collaboration contracts.
Source map
| Source file | Relevant symbols |
|---|---|
HEVC-Videos-With-Alpha-Playback/HEVC-Videos-With-Alpha-Playback/ViewController.swift |
SpriteKit/AVPlayer playback |
HEVC-Videos-With-Alpha-AssetWriting/HEVC-Videos-With-Alpha-AssetWriting/AppDelegate.swift |
Scene rendering and asset writing |
HEVC-Videos-With-Alpha-Exports/HEVC-Videos-With-Alpha-Exports/main.swift |
Export recipes and chroma key |
HEVC-Videos-With-Alpha.xcworkspace/contents.xcworkspacedata |
Three-project composition |
Source caveat: the targets intentionally duplicate small amounts of setup and do not form a reusable HEVC-alpha library.