Skip to content

Instantly share code, notes, and snippets.

@starkdmi
Last active June 11, 2023 13:00
Show Gist options
  • Save starkdmi/6a67b1fb2883cd9cebc1aafb0235f67e to your computer and use it in GitHub Desktop.
Save starkdmi/6a67b1fb2883cd9cebc1aafb0235f67e to your computer and use it in GitHub Desktop.
Lossless Video Cut using AVAssetWriter
let source = URL(fileURLWithPath: "input.mov")
let destination = URL(fileURLWithPath: "output.mov")
try? FileManager.default.removeItem(at: destination)
let asset = AVAsset(url: source)
let reader = try! AVAssetReader(asset: asset)
let writer = try! AVAssetWriter(outputURL: destination, fileType: .mov)
let videoTrack = await asset.getFirstTrack(withMediaType: .video)
let videoOutput = AVAssetReaderTrackOutput(track: videoTrack!, outputSettings: nil)
let videoInput = AVAssetWriterInput(mediaType: .video, outputSettings: nil)
reader.add(videoOutput)
writer.add(videoInput)
videoInput.transform = videoTrack!.fixedPreferredTransform
// Time Range and Duration
let timeScale = videoTrack!.naturalTimeScale
let startTime: CMTime = CMTime(seconds: 3, preferredTimescale: timeScale)
let endTime: CMTime = CMTime(seconds: 5, preferredTimescale: timeScale) // asset.duration.seconds
reader.timeRange = CMTimeRange(start: startTime, end: endTime)
writer.shouldOptimizeForNetworkUse = true
reader.startReading()
writer.startWriting()
// writer.startSession(atSourceTime: .zero)
writer.startSession(atSourceTime: startTime)
let semathore = DispatchSemaphore(value: 0)
let videoQueue = DispatchQueue(label: "LosslessVideoCutQueue")
videoInput.requestMediaDataWhenReady(on: videoQueue) {
while videoInput.isReadyForMoreMediaData {
if let sampleBuffer = videoOutput.copyNextSampleBuffer() {
// If starting session from .zero is required in your case (`writer.startSession(atSourceTime: .zero)`) use
/*let presentationTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
let newPresentationTime = CMTimeSubtract(presentationTime, startTime)
let status = CMSampleBufferSetOutputPresentationTimeStamp(sampleBuffer, newValue: newPresentationTime)
if status == noErr {
videoInput.append(sampleBuffer)
}*/
// instead of:
videoInput.append(sampleBuffer)
} else {
videoInput.markAsFinished()
writer.finishWriting {
if writer.status == .completed {
semathore.signal()
} else {
fatalError(writer.error?.localizedDescription ?? "Unknown error")
}
}
}
}
}
semathore.wait()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment