问题
I am following the below code for square video composition
func completeWithVideoAtURL(input: NSURL) {
let asset = AVAsset(url: input as URL)
let output = NSURL(fileURLWithPath: NSHomeDirectory() + "/Documents/Video.mp4")
let session = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetMediumQuality)!
session.videoComposition = self.squareVideoCompositionForAsset(asset: asset)
session.outputURL = output as URL
session.outputFileType = AVFileTypeMPEG4
session.shouldOptimizeForNetworkUse = true
session.exportAsynchronously(completionHandler: { () -> Void in
DispatchQueue.main.async(execute: { () -> Void in
// do something with the output
print("\(output)")
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: output as URL)
}) { saved, error in
if saved {
print("saved to gallery")
}
}
})
})
}
func squareVideoCompositionForAsset(asset: AVAsset) -> AVVideoComposition {
let track = asset.tracks(withMediaType: AVMediaTypeVideo)[0]
let length = max(track.naturalSize.width, track.naturalSize.height)
var transform = track.preferredTransform
let size = track.naturalSize
let scale: CGFloat = (transform.a == -1 && transform.b == 0 && transform.c == 0 && transform.d == -1) ? -1 : 1 // check for inversion
transform = transform.translatedBy(x: scale * -(size.width - length) / 2, y: scale * -(size.height - length) / 2)
let transformer = AVMutableVideoCompositionLayerInstruction(assetTrack: track)
transformer.setTransform(transform, at: kCMTimeZero)
let instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = CMTimeRange(start: kCMTimeZero, duration: kCMTimePositiveInfinity)
instruction.layerInstructions = [transformer]
let composition = AVMutableVideoComposition()
composition.frameDuration = CMTime(value: 1, timescale: 30)
composition.renderSize = CGSize(width: length, height: length)
composition.instructions = [instruction]
return composition
}
From the squareVideoCompositionForAsset() function I take the max value for length between track.naturalSize.width & track.naturalSize.height cause I don't want to crop any partial part of the video. If I take min value, for portrait video it cropped the upper & lower portion of the video and for landscape video it cropped some left & right portion of the video.
- For landscape video, output is okay
- but for portrait video, output is like following image
the video gets left sided. Is it possible to center the video? Any assistance would be great and sorry for long explanation.
回答1:
instead of this line
let scale: CGFloat = (transform.a == -1 && transform.b == 0 && transform.c == 0 && transform.d == -1) ? -1 : 1
I just used this
var scale = CGFloat() if (transform.a == 0 && transform.b == 1 && transform.c == -1 && transform.d == 0) { scale = -1 } else if (transform.a == 0 && transform.b == -1 && transform.c == 1 && transform.d == 0) { scale = -1 } else if (transform.a == 1 && transform.b == 0 && transform.c == 0 && transform.d == 1) { scale = 1 } else if (transform.a == -1 && transform.b == 0 && transform.c == 0 && transform.d == -1) { scale = 1 }
and it worked like a charm
回答2:
Swift 4.2
func suqareCropVideo(videoURL: URL, withSide sideLength: CGFloat, completion: @escaping (_ resultURL: URL?, _ error: Error?) -> ()) {
let asset = AVAsset(url: videoURL)
if let assetVideoTrack = asset.tracks(withMediaType: .video).last {
let originalSize = assetVideoTrack.naturalSize
var scale: CGFloat
if originalSize.width < originalSize.height {
scale = sideLength / originalSize.width
} else {
scale = sideLength / originalSize.height
}
let scaledSize = CGSize(width: originalSize.width * scale, height: originalSize.height * scale)
let topLeft = CGPoint(x: sideLength * 0.5 - scaledSize.width * 0.5, y: sideLength * 0.5 - scaledSize.height * 0.5)
let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: assetVideoTrack)
var orientationTransform = assetVideoTrack.preferredTransform
if (orientationTransform.tx == originalSize.width || orientationTransform.tx == originalSize.height) {
orientationTransform.tx = sideLength
}
if (orientationTransform.ty == originalSize.width || orientationTransform.ty == originalSize.height) {
orientationTransform.ty = sideLength
}
let transform = CGAffineTransform(scaleX: scale, y: scale).concatenating(CGAffineTransform(translationX: topLeft.x, y: topLeft.y)).concatenating(orientationTransform)
layerInstruction.setTransform(transform, at: .zero)
let instruction = AVMutableVideoCompositionInstruction()
instruction.layerInstructions = [layerInstruction]
instruction.timeRange = assetVideoTrack.timeRange
let videoComposition = AVMutableVideoComposition()
videoComposition.renderSize = CGSize(width: sideLength, height: sideLength)
videoComposition.renderScale = 1.0
videoComposition.frameDuration = CMTime(value: 1, timescale: 30)
videoComposition.instructions = [instruction]
if let export = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) {
export.videoComposition = videoComposition
export.outputURL = NSURL.fileURL(withPath: "\(NSTemporaryDirectory())\(NSUUID().uuidString).mp4")
export.outputFileType = AVFileType.mp4
export.shouldOptimizeForNetworkUse = true
export.exportAsynchronously {
DispatchQueue.main.async {
if export.status == .completed {
completion(export.outputURL, nil)
} else {
completion(nil, export.error)
}
}
}
} else {
completion(nil, nil)
}
}
}
来源:https://stackoverflow.com/questions/43558021/swift-square-video-composition