mirror of
https://github.com/samsonjs/SJSAssetExportSession.git
synced 2026-04-27 14:57:46 +00:00
Add support for writing metadata on the asset
This commit is contained in:
parent
8f049aed95
commit
8e46d79376
3 changed files with 44 additions and 0 deletions
|
|
@ -25,6 +25,7 @@ public final class ExportSession: @unchecked Sendable {
|
||||||
public func export(
|
public func export(
|
||||||
asset: sending AVAsset,
|
asset: sending AVAsset,
|
||||||
optimizeForNetworkUse: Bool = false,
|
optimizeForNetworkUse: Bool = false,
|
||||||
|
metadata: sending [AVMetadataItem] = [],
|
||||||
timeRange: CMTimeRange? = nil,
|
timeRange: CMTimeRange? = nil,
|
||||||
audio: sending AudioOutputSettings = .default,
|
audio: sending AudioOutputSettings = .default,
|
||||||
video: sending VideoOutputSettings,
|
video: sending VideoOutputSettings,
|
||||||
|
|
@ -42,6 +43,7 @@ public final class ExportSession: @unchecked Sendable {
|
||||||
videoComposition: videoComposition,
|
videoComposition: videoComposition,
|
||||||
timeRange: timeRange,
|
timeRange: timeRange,
|
||||||
optimizeForNetworkUse: optimizeForNetworkUse,
|
optimizeForNetworkUse: optimizeForNetworkUse,
|
||||||
|
metadata: metadata,
|
||||||
outputURL: outputURL,
|
outputURL: outputURL,
|
||||||
fileType: fileType
|
fileType: fileType
|
||||||
)
|
)
|
||||||
|
|
@ -61,6 +63,8 @@ public final class ExportSession: @unchecked Sendable {
|
||||||
|
|
||||||
- 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`.
|
- 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.
|
- timeRange: Providing a time range exports a subset of the asset instead of the entire duration, which is the default behaviour.
|
||||||
|
|
||||||
- audioOutputSettings: Audio settings using [audio settings keys from AVFoundation](https://developer.apple.com/documentation/avfoundation/audio_settings) and values must be suitable for consumption by Objective-C. Required keys are:
|
- audioOutputSettings: Audio settings using [audio settings keys from AVFoundation](https://developer.apple.com/documentation/avfoundation/audio_settings) and values must be suitable for consumption by Objective-C. Required keys are:
|
||||||
|
|
@ -83,6 +87,7 @@ public final class ExportSession: @unchecked Sendable {
|
||||||
public func export(
|
public func export(
|
||||||
asset: sending AVAsset,
|
asset: sending AVAsset,
|
||||||
optimizeForNetworkUse: Bool = false,
|
optimizeForNetworkUse: Bool = false,
|
||||||
|
metadata: sending [AVMetadataItem] = [],
|
||||||
timeRange: CMTimeRange? = nil,
|
timeRange: CMTimeRange? = nil,
|
||||||
audioOutputSettings: [String: (any Sendable)],
|
audioOutputSettings: [String: (any Sendable)],
|
||||||
mix: sending AVAudioMix? = nil,
|
mix: sending AVAudioMix? = nil,
|
||||||
|
|
@ -118,6 +123,7 @@ public final class ExportSession: @unchecked Sendable {
|
||||||
videoComposition: videoComposition,
|
videoComposition: videoComposition,
|
||||||
timeRange: timeRange,
|
timeRange: timeRange,
|
||||||
optimizeForNetworkUse: optimizeForNetworkUse,
|
optimizeForNetworkUse: optimizeForNetworkUse,
|
||||||
|
metadata: metadata,
|
||||||
outputURL: outputURL,
|
outputURL: outputURL,
|
||||||
fileType: fileType
|
fileType: fileType
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ actor SampleWriter {
|
||||||
videoComposition: sending AVVideoComposition,
|
videoComposition: sending AVVideoComposition,
|
||||||
timeRange: CMTimeRange? = nil,
|
timeRange: CMTimeRange? = nil,
|
||||||
optimizeForNetworkUse: Bool = false,
|
optimizeForNetworkUse: Bool = false,
|
||||||
|
metadata: [AVMetadataItem] = [],
|
||||||
outputURL: URL,
|
outputURL: URL,
|
||||||
fileType: AVFileType
|
fileType: AVFileType
|
||||||
) async throws {
|
) async throws {
|
||||||
|
|
@ -66,6 +67,7 @@ actor SampleWriter {
|
||||||
}
|
}
|
||||||
let writer = try AVAssetWriter(outputURL: outputURL, fileType: fileType)
|
let writer = try AVAssetWriter(outputURL: outputURL, fileType: fileType)
|
||||||
writer.shouldOptimizeForNetworkUse = optimizeForNetworkUse
|
writer.shouldOptimizeForNetworkUse = optimizeForNetworkUse
|
||||||
|
writer.metadata = metadata
|
||||||
|
|
||||||
let audioTracks = try await asset.loadTracks(withMediaType: .audio)
|
let audioTracks = try await asset.loadTracks(withMediaType: .audio)
|
||||||
// Audio is optional so only validate output settings when it's applicable.
|
// Audio is optional so only validate output settings when it's applicable.
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
internal import AVFoundation
|
internal import AVFoundation
|
||||||
|
import CoreLocation
|
||||||
@testable import SJSAssetExportSession
|
@testable import SJSAssetExportSession
|
||||||
import Testing
|
import Testing
|
||||||
|
|
||||||
|
|
@ -321,4 +322,39 @@ final class ExportSessionTests {
|
||||||
try? await task.value // Wait for task to complete
|
try? await task.value // Wait for task to complete
|
||||||
NSLog("Task has finished executing")
|
NSLog("Task has finished executing")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test func test_writing_metadata() async throws {
|
||||||
|
let sourceURL = resourceURL(named: "test-720p-h264-24fps", withExtension: "mov")
|
||||||
|
let destinationURL = makeTemporaryURL()
|
||||||
|
let locationMetadata = AVMutableMetadataItem()
|
||||||
|
locationMetadata.key = AVMetadataKey.commonKeyLocation.rawValue as NSString
|
||||||
|
locationMetadata.keySpace = .common
|
||||||
|
locationMetadata.value = "+48.50176+123.34368/" as NSString
|
||||||
|
|
||||||
|
let subject = ExportSession()
|
||||||
|
try await subject.export(
|
||||||
|
asset: makeAsset(url: sourceURL),
|
||||||
|
metadata: [locationMetadata],
|
||||||
|
video: .codec(.h264, size: CGSize(width: 1280, height: 720)),
|
||||||
|
to: destinationURL.url,
|
||||||
|
as: .mov
|
||||||
|
)
|
||||||
|
|
||||||
|
let exportedAsset = AVURLAsset(url: destinationURL.url)
|
||||||
|
let exportedMetadata = try await exportedAsset.load(.metadata)
|
||||||
|
print(exportedMetadata)
|
||||||
|
#expect(exportedMetadata.count == 1)
|
||||||
|
let metadataValue = try await exportedMetadata.first(where: { item in
|
||||||
|
item.key as! String == AVMetadataKey.quickTimeMetadataKeyLocationISO6709.rawValue
|
||||||
|
})?.load(.value) as? NSString
|
||||||
|
#expect(metadataValue == "+48.50176+123.34368/")
|
||||||
|
|
||||||
|
let exportedCommonMetadata = try await exportedAsset.load(.commonMetadata)
|
||||||
|
print(exportedCommonMetadata)
|
||||||
|
#expect(exportedCommonMetadata.count == 1)
|
||||||
|
let commonMetadataValue = try await exportedCommonMetadata.first(where: { item in
|
||||||
|
item.commonKey == .commonKeyLocation
|
||||||
|
})?.load(.value) as? NSString
|
||||||
|
#expect(commonMetadataValue == "+48.50176+123.34368/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue