mirror of
https://github.com/samsonjs/SJSAssetExportSession.git
synced 2026-03-25 08:45:50 +00:00
Flesh out documentation for most of the public API
This commit is contained in:
parent
16599d638c
commit
b60032f15f
9 changed files with 128 additions and 39 deletions
36
Readme.md
36
Readme.md
|
|
@ -1,25 +1,31 @@
|
|||
# Overview
|
||||
# SJSAssetExportSession
|
||||
|
||||
## Overview
|
||||
|
||||
`SJSAssetExportSession` is an alternative to [`AVAssetExportSession`][AV] that lets you provide custom audio and video settings, without dropping down into the world of `AVAssetReader` and `AVAssetWriter`. It has similar capabilites to [SDAVAssetExportSession][SDAV] but the API is completely different, the code is written in Swift, and it's ready for the world of strict concurrency.
|
||||
|
||||
You shouldn't have to read through [audio settings][] and [video settings][] just to set the bitrate, and setting the frame rate can be tricky, so there's a nicer API that builds these settings dictionaries with some commonly used settings.
|
||||
|
||||
[AV]: https://developer.apple.com/documentation/avfoundation/avassetexportsession
|
||||
[SDAV]: https://github.com/rs/SDAVAssetExportSession
|
||||
[audio settings]: https://developer.apple.com/documentation/avfoundation/audio_settings
|
||||
[video settings]: https://developer.apple.com/documentation/avfoundation/video_settings
|
||||
|
||||
# Installation
|
||||
## Installation
|
||||
|
||||
The only way to install this package is with Swift Package Manager (SPM). Please [file a new issue][] or submit a pull-request if you want to use something else.
|
||||
|
||||
[file a new issue]: https://github.com/samsonjs/SJSAssetExportSession/issues/new
|
||||
|
||||
## Supported Platforms
|
||||
### Supported Platforms
|
||||
|
||||
This package is supported on iOS 17.0+, macOS Sonoma 14.0+, and visionOS 1.3+.
|
||||
|
||||
## Xcode
|
||||
### Xcode
|
||||
|
||||
When you're integrating this into an app with Xcode then go to your project's Package Dependencies and enter the URL `https://github.com/samsonjs/SJSAssetExportSession` and then go through the usual flow for adding packages.
|
||||
|
||||
## Swift Package Manager (SPM)
|
||||
### Swift Package Manager (SPM)
|
||||
|
||||
When you're integrating this using SPM on its own then add this to your Package.swift file:
|
||||
|
||||
|
|
@ -27,11 +33,11 @@ When you're integrating this using SPM on its own then add this to your Package.
|
|||
.package(url: "https://github.com/samsonjs/SJSAssetExportSession.git", .upToNextMajor(from: "1.0"))
|
||||
```
|
||||
|
||||
# Usage
|
||||
## Usage
|
||||
|
||||
There are two ways of exporting assets: one using dictionaries for audio and video settings just like with `SDAVAssetExportSession`, and the other using a builder-like API with data structures for commonly used settings.
|
||||
|
||||
## The Nice Way
|
||||
### The Nice Way
|
||||
|
||||
This should be fairly self-explanatory:
|
||||
|
||||
|
|
@ -58,7 +64,7 @@ try await exporter.export(
|
|||
|
||||
Most of the audio and video configuration is optional which is why there are no audio settings specified here. By default you get AAC with 2 channels at a 44.1 KHz sample rate.
|
||||
|
||||
## All Nice Parameters
|
||||
### All Nice Parameters
|
||||
|
||||
Here are all of the parameters you can pass into the nice export method:
|
||||
|
||||
|
|
@ -94,7 +100,7 @@ try await exporter.export(
|
|||
)
|
||||
```
|
||||
|
||||
## The Most Flexible Way
|
||||
### The Most Flexible Way
|
||||
|
||||
When you need all the control you can get down to the nitty gritty details. This code does the exact same thing as the code above:
|
||||
|
||||
|
|
@ -138,7 +144,7 @@ try await exporter.export(
|
|||
AVVideoCompressionPropertiesKey: [
|
||||
AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
|
||||
AVVideoAverageBitRateKey: NSNumber(value: 1_000_000),
|
||||
] as [String: (any Sendable)],
|
||||
] as [String: any Sendable],
|
||||
AVVideoColorPropertiesKey: [
|
||||
AVVideoColorPrimariesKey: AVVideoColorPrimaries_ITU_R_709_2,
|
||||
AVVideoTransferFunctionKey: AVVideoTransferFunction_ITU_R_709_2,
|
||||
|
|
@ -151,13 +157,9 @@ try await exporter.export(
|
|||
)
|
||||
```
|
||||
|
||||
It's an effective illustration of why the nicer API exists right? You shouldn't have to read through [audio settings][] and [video settings][] just to set the bitrate, and setting the frame rate can be tricky. But when you need this flexibility then it's available for you.
|
||||
It's an effective illustration of why the nicer API exists right? But when you need this flexibility then it's available for you.
|
||||
|
||||
[audio settings]: https://developer.apple.com/documentation/avfoundation/audio_settings
|
||||
|
||||
[video settings]: https://developer.apple.com/documentation/avfoundation/video_settings
|
||||
|
||||
## Mix and Match
|
||||
### Mix and Match
|
||||
|
||||
`AudioOutputSettings` and `VideoOutputSettings` have a property named `settingsDictionary` and you can use that to bootstrap your own custom settings.
|
||||
|
||||
|
|
@ -188,7 +190,7 @@ try await exporter.export(
|
|||
)
|
||||
```
|
||||
|
||||
# License
|
||||
## License
|
||||
|
||||
Copyright © 2024 Sami Samhuri, https://samhuri.net <sami@samhuri.net>. Released under the terms of the [MIT License][MIT].
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,15 @@
|
|||
|
||||
public import AVFoundation
|
||||
|
||||
/// A convenient API for constructing audio settings dictionaries.
|
||||
///
|
||||
/// Construct this by starting with ``AudioOutputSettings/default`` or ``AudioOutputSettings/format(_:)`` and then chain calls to further customize it, if desired, using ``channels(_:)``, ``sampleRate(_:)``, and ``mix(_:)``.
|
||||
public struct AudioOutputSettings {
|
||||
/// Describes the output file format.
|
||||
public enum Format {
|
||||
/// Advanced Audio Codec. The audio format typically used for MPEG-4 audio.
|
||||
case aac
|
||||
/// The MPEG Layer 3 audio format.
|
||||
case mp3
|
||||
|
||||
var formatID: AudioFormatID {
|
||||
|
|
@ -25,10 +31,12 @@ public struct AudioOutputSettings {
|
|||
let sampleRate: Int?
|
||||
let mix: AVAudioMix?
|
||||
|
||||
/// Specifies the AAC format with 2 channels at a 44.1 KHz sample rate.
|
||||
public static var `default`: AudioOutputSettings {
|
||||
.format(.aac).channels(2).sampleRate(44_100)
|
||||
}
|
||||
|
||||
/// Specifies the given format with 2 channels.
|
||||
public static func format(_ format: Format) -> AudioOutputSettings {
|
||||
.init(format: format.formatID, channels: 2, sampleRate: nil, mix: nil)
|
||||
}
|
||||
|
|
@ -45,7 +53,7 @@ public struct AudioOutputSettings {
|
|||
.init(format: format, channels: channels, sampleRate: sampleRate, mix: mix)
|
||||
}
|
||||
|
||||
var settingsDictionary: [String: any Sendable] {
|
||||
public var settingsDictionary: [String: any Sendable] {
|
||||
if let sampleRate {
|
||||
[
|
||||
AVFormatIDKey: format,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,28 @@ public final class ExportSession: Sendable {
|
|||
(progressStream, progressContinuation) = AsyncStream<Float>.makeStream()
|
||||
}
|
||||
|
||||
/**
|
||||
Exports the given asset using all of the other parameters to transform it in some way. This method uses code to build up audio and video settings with a nice API instead of diving into the nitty gritty settings dictionaries. Monitor progress using ``progressStream``.
|
||||
|
||||
- Parameters:
|
||||
- asset: The source asset to export. This can be any kind of `AVAsset` including subclasses such as `AVComposition`.
|
||||
|
||||
- optimizeForNetworkUse: Setting this value to `true` writes the output file in a form that enables a player to begin playing the media after downloading only a small portion of it. Defaults to `false`.
|
||||
|
||||
- metadata: Optional array of `AVMetadataItem`s to be written out with the exported asset.
|
||||
|
||||
- timeRange: Providing a time range exports a subset of the asset instead of the entire duration, which is the default behaviour.
|
||||
|
||||
- audio: Optional audio settings using ``AudioOutputSettings``. Defaults to ``AudioOutputSettings/default``.
|
||||
|
||||
- video: Video settings using ``VideoOutputSettings``.
|
||||
|
||||
- outputURL: The file `URL` where the exported video will be written.
|
||||
|
||||
- fileType: The type of of video file to export. This will typically be one of `AVFileType.mp4`, `AVFileType.m4v`, or `AVFileType.mov`.
|
||||
|
||||
- Throws: One of the cases in the ``ExportSession/Error`` enum when the export fails. See ``ExportSession/Error`` for possible failures.
|
||||
*/
|
||||
public func export(
|
||||
asset: sending AVAsset,
|
||||
optimizeForNetworkUse: Bool = false,
|
||||
|
|
@ -50,7 +72,7 @@ public final class ExportSession: Sendable {
|
|||
}
|
||||
|
||||
/**
|
||||
Exports the given asset using all of the other parameters to transform it in some way.
|
||||
Exports the given asset using all of the other parameters to transform it in some way. This method provides the most control over the export using audio and video settings dictionaries, in addition to an optionial audio mix and optional video composition. Monitor progress using ``progressStream``.
|
||||
|
||||
- Parameters:
|
||||
- asset: The source asset to export. This can be any kind of `AVAsset` including subclasses such as `AVComposition`.
|
||||
|
|
@ -77,15 +99,17 @@ public final class ExportSession: Sendable {
|
|||
- outputURL: The file URL where the exported video will be written.
|
||||
|
||||
- fileType: The type of of video file to export. This will typically be one of `AVFileType.mp4`, `AVFileType.m4v`, or `AVFileType.mov`.
|
||||
|
||||
- Throws: One of the cases in the ``ExportSession/Error`` enum when the export fails. See ``ExportSession/Error`` for possible failures.
|
||||
*/
|
||||
public func export(
|
||||
asset: sending AVAsset,
|
||||
optimizeForNetworkUse: Bool = false,
|
||||
metadata: sending [AVMetadataItem] = [],
|
||||
timeRange: CMTimeRange? = nil,
|
||||
audioOutputSettings: [String: (any Sendable)],
|
||||
audioOutputSettings: [String: any Sendable],
|
||||
mix: sending AVAudioMix? = nil,
|
||||
videoOutputSettings: [String: (any Sendable)],
|
||||
videoOutputSettings: [String: any Sendable],
|
||||
composition: sending AVVideoComposition? = nil,
|
||||
to outputURL: URL,
|
||||
as fileType: AVFileType
|
||||
|
|
|
|||
|
|
@ -1,13 +1,49 @@
|
|||
# ``SJSAssetExportSession``
|
||||
|
||||
<!--@START_MENU_TOKEN@-->Summary<!--@END_MENU_TOKEN@-->
|
||||
`SJSAssetExportSession` is an alternative to `AVAssetExportSession` that lets you provide custom audio and video settings, without dropping down into the world of `AVAssetReader` and `AVAssetWriter`.
|
||||
|
||||
## Overview
|
||||
[`AVAssetExportSession`][AV] is fine for some things but it provides basically no way to customize the export settings, besides the couple of options on `AVVideoComposition` like render size and frame rate. This package has similar capabilites to the venerable [`SDAVAssetExportSession`][SDAV] but the API is completely different, the code is written in Swift, and it's ready for the world of strict concurrency.
|
||||
|
||||
<!--@START_MENU_TOKEN@-->Text<!--@END_MENU_TOKEN@-->
|
||||
You shouldn't have to read through [audio settings][] and [video settings][] just to set the bitrate, and setting the frame rate can be tricky, so there's a nicer API that builds these settings dictionaries with some commonly used settings.
|
||||
|
||||
[AV]: https://developer.apple.com/documentation/avfoundation/avassetexportsession
|
||||
[SDAV]: https://github.com/rs/SDAVAssetExportSession
|
||||
[audio settings]: https://developer.apple.com/documentation/avfoundation/audio_settings
|
||||
[video settings]: https://developer.apple.com/documentation/avfoundation/video_settings
|
||||
|
||||
The simplest usage is something like this:
|
||||
|
||||
```swift
|
||||
let exporter = ExportSession()
|
||||
Task {
|
||||
for await progress in exporter.progressStream {
|
||||
print("Progress: \(progress)")
|
||||
}
|
||||
}
|
||||
try await exporter.export(
|
||||
asset: AVURLAsset(url: sourceURL, options: [AVURLAssetPreferPreciseDurationAndTimingKey: true]),
|
||||
video: .codec(.h264, width: 1280, height: 720),
|
||||
to: URL.temporaryDirectory.appeding(component: "new-video.mp4"),
|
||||
as: .mp4
|
||||
)
|
||||
```
|
||||
|
||||
## Topics
|
||||
|
||||
### <!--@START_MENU_TOKEN@-->Group<!--@END_MENU_TOKEN@-->
|
||||
### Exporting
|
||||
|
||||
- <!--@START_MENU_TOKEN@-->``Symbol``<!--@END_MENU_TOKEN@-->
|
||||
- ``ExportSession``
|
||||
- ``ExportSession/Error``
|
||||
- ``ExportSession/SetupFailureReason``
|
||||
|
||||
### Audio Output Settings
|
||||
|
||||
- ``AudioOutputSettings``
|
||||
- ``AudioOutputSettings/Format``
|
||||
|
||||
### Video Output Settings
|
||||
|
||||
- ``VideoOutputSettings``
|
||||
- ``VideoOutputSettings/Codec``
|
||||
- ``VideoOutputSettings/H264Profile``
|
||||
- ``VideoOutputSettings/Color``
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ actor SampleWriter {
|
|||
}
|
||||
private var progressContinuation: AsyncStream<Float>.Continuation?
|
||||
|
||||
private let audioOutputSettings: [String: (any Sendable)]
|
||||
private let audioOutputSettings: [String: any Sendable]
|
||||
private let audioMix: AVAudioMix?
|
||||
private let videoOutputSettings: [String: (any Sendable)]
|
||||
private let videoOutputSettings: [String: any Sendable]
|
||||
private let videoComposition: AVVideoComposition?
|
||||
private let reader: AVAssetReader
|
||||
private let writer: AVAssetWriter
|
||||
|
|
@ -48,9 +48,9 @@ actor SampleWriter {
|
|||
|
||||
nonisolated init(
|
||||
asset: sending AVAsset,
|
||||
audioOutputSettings: sending [String: (any Sendable)],
|
||||
audioOutputSettings: sending [String: any Sendable],
|
||||
audioMix: sending AVAudioMix?,
|
||||
videoOutputSettings: sending [String: (any Sendable)],
|
||||
videoOutputSettings: sending [String: any Sendable],
|
||||
videoComposition: sending AVVideoComposition,
|
||||
timeRange: CMTimeRange? = nil,
|
||||
optimizeForNetworkUse: Bool = false,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,13 @@
|
|||
|
||||
import AVFoundation
|
||||
|
||||
/// A convenient API for constructing video settings dictionaries.
|
||||
///
|
||||
/// Construct this by starting with ``VideoOutputSettings/codec(_:size:)`` or ``VideoOutputSettings/codec(_:width:height:)`` and then chaining calls to further customize it, if desired, using ``fps(_:)``, ``bitrate(_:)``, and ``color(_:)``.
|
||||
///
|
||||
/// Setting the fps and colour also needs support from the `AVVideoComposition` and these settings can be applied to them with ``VideoOutputSettings/apply(to:)``.
|
||||
public struct VideoOutputSettings {
|
||||
/// Describes an H.264 encoding profile.
|
||||
public enum H264Profile {
|
||||
case baselineAuto, baseline30, baseline31, baseline41
|
||||
case mainAuto, main31, main32, main41
|
||||
|
|
@ -30,11 +36,15 @@ public struct VideoOutputSettings {
|
|||
}
|
||||
}
|
||||
|
||||
/// Specifies the output codec.
|
||||
public enum Codec {
|
||||
/// H.264 using the associated encoding profile.
|
||||
case h264(H264Profile)
|
||||
/// HEVC / H.265
|
||||
case hevc
|
||||
|
||||
static var h264: Codec {
|
||||
/// Construct Codec.h264 using the default profile `H264Profile.highAuto`.
|
||||
public static var h264: Codec {
|
||||
.h264(.highAuto)
|
||||
}
|
||||
|
||||
|
|
@ -53,8 +63,12 @@ public struct VideoOutputSettings {
|
|||
}
|
||||
}
|
||||
|
||||
/// Specifies whether to use Standard Dynamic Range or High Dynamic Range colours.
|
||||
public enum Color {
|
||||
case sdr, hdr
|
||||
/// Standard dynamic range colours (BT.709 which roughly corresponds to SRGB)
|
||||
case sdr
|
||||
/// High dynamic range colours (BT.2020)
|
||||
case hdr
|
||||
|
||||
var properties: [String: any Sendable] {
|
||||
switch self {
|
||||
|
|
@ -100,7 +114,7 @@ public struct VideoOutputSettings {
|
|||
.init(codec: codec, size: size, fps: fps, bitrate: bitrate, color: color)
|
||||
}
|
||||
|
||||
var settingsDictionary: [String: any Sendable] {
|
||||
public var settingsDictionary: [String: any Sendable] {
|
||||
var result: [String: any Sendable] = [
|
||||
AVVideoCodecKey: codec.stringValue,
|
||||
AVVideoWidthKey: NSNumber(value: Int(size.width)),
|
||||
|
|
@ -121,6 +135,11 @@ public struct VideoOutputSettings {
|
|||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/// Applies the subset of relevant settings to the given video composition, namely fps and colour.
|
||||
public func apply(to videoComposition: AVMutableVideoComposition) {
|
||||
_ = videoComposition.applyingSettings(self)
|
||||
}
|
||||
}
|
||||
|
||||
extension AVMutableVideoComposition {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
// Created by Sami Samhuri on 2024-07-07.
|
||||
//
|
||||
|
||||
internal import AVFoundation
|
||||
import AVFoundation
|
||||
|
||||
extension AVAsset {
|
||||
func sendTracks(withMediaType mediaType: AVMediaType) async throws -> sending [AVAssetTrack] {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
// Created by Sami Samhuri on 2024-08-18.
|
||||
//
|
||||
|
||||
internal import AVFoundation
|
||||
@testable import SJSAssetExportSession
|
||||
import AVFoundation
|
||||
import SJSAssetExportSession
|
||||
|
||||
private func readmeNiceExample() async throws {
|
||||
let sourceURL = URL.documentsDirectory.appending(component: "some-video.mov")
|
||||
|
|
@ -101,7 +101,7 @@ private func readmeFlexibleExample() async throws {
|
|||
AVVideoCompressionPropertiesKey: [
|
||||
AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
|
||||
AVVideoAverageBitRateKey: NSNumber(value: 1_000_000),
|
||||
] as [String: (any Sendable)],
|
||||
] as [String: any Sendable],
|
||||
AVVideoColorPropertiesKey: [
|
||||
AVVideoColorPrimariesKey: AVVideoColorPrimaries_ITU_R_709_2,
|
||||
AVVideoTransferFunctionKey: AVVideoTransferFunction_ITU_R_709_2,
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
// Created by Sami Samhuri on 2024-06-29.
|
||||
//
|
||||
|
||||
internal import AVFoundation
|
||||
import AVFoundation
|
||||
import CoreLocation
|
||||
@testable import SJSAssetExportSession
|
||||
import SJSAssetExportSession
|
||||
import Testing
|
||||
|
||||
final class ExportSessionTests {
|
||||
|
|
|
|||
Loading…
Reference in a new issue