I am trying to crop the video in landscape mode using AVKit. I have tried several similar questions on Stackoverflow but none is working as expected. This is what I have tried so far.
self.cropOfficialRect = CGRect(x: 0, y: (reader.size.height/2)-100-self.currentPosition.height, width: UIScreen.main.bounds.width, height: 200)
let assest = AVAsset(url: self.videoURL)
let videoTrack = assest.tracks(withMediaType: .video)[0]
let originalSize = videoTrack.naturalSize
let cropRectIsPortrait = originalSize.width <= originalSize.height
if cropRectIsPortrait{
self.videoViewScale = max(originalSize.width / reader.size.width, originalSize.height / reader.size.height)
}
else{
}
extension AVAsset {
func cropVideoTrack(at index: Int, cropRect: CGRect, outputURL: URL, completion: @escaping (Result<Void, Swift.Error>) -> Void) {
enum Orientation {
case up, down, right, left
}
func orientation(for track: AVAssetTrack) -> Orientation {
let t = track.preferredTransform
if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0) { // Portrait
return .up
} else if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0) { // PortraitUpsideDown
return .down
} else if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0) { // LandscapeRight
return .right
} else if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0) { // LandscapeLeft
return .left
} else {
return .up
}
}
let videoTrack = tracks(withMediaType: .video)[index]
let originalSize = videoTrack.naturalSize
let trackOrientation = orientation(for: videoTrack)
let cropRectIsPortrait = cropRect.width <= cropRect.height
let videoComposition = AVMutableVideoComposition()
videoComposition.renderSize = cropRect.size
videoComposition.frameDuration = CMTime(value: 1, timescale: 30)
let layerinstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)
let instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = CMTimeRange(start: .zero, duration: CMTime(seconds: 10000, preferredTimescale: 30))
layerinstruction.setCropRectangle(cropRect, at: .zero)//cropping Rectangle
let transformer = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)
var finalTransform: CGAffineTransform = CGAffineTransform.identity // setup a transform that grows the video, effectively causing a crop
if trackOrientation == .up {
if !cropRectIsPortrait { // center video rect vertically
finalTransform = finalTransform
//.translatedBy(x: originalSize.height, y: -(originalSize.width - cropRect.size.height) / 2)
.translatedBy(x: originalSize.height, y: 0)
.rotated(by: CGFloat(90.0.radians))
} else {
finalTransform = finalTransform
.rotated(by: CGFloat(90.0.radians))
.translatedBy(x: 0, y: -originalSize.height)
}
} else if trackOrientation == .down {
if !cropRectIsPortrait { // center video rect vertically (NOTE: did not test this case, since camera doesn't support .portraitUpsideDown in this app)
finalTransform = finalTransform
.translatedBy(x: -originalSize.height, y: (originalSize.width - cropRect.size.height) / 2)
.rotated(by: CGFloat(-90.0.radians))
} else {
finalTransform = finalTransform
.rotated(by: CGFloat(-90.0.radians))
.translatedBy(x: -originalSize.width, y: -(originalSize.height - cropRect.size.height) / 2)
}
} else if trackOrientation == .right {
if cropRectIsPortrait {
finalTransform = finalTransform.translatedBy(x: -(originalSize.width - cropRect.size.width) / 2, y: 0)
} else {
//finalTransform = CGAffineTransform(
finalTransform = CGAffineTransform(translationX: -(originalSize.width - cropRect.size.width) / 2, y: -cropRect.minY)
}
} else if trackOrientation == .left {
if cropRectIsPortrait { // center video rect horizontally
finalTransform = finalTransform
.rotated(by: CGFloat(-180.0.radians))
.translatedBy(x: -originalSize.width + (originalSize.width - cropRect.size.width) / 2, y: -originalSize.height)
} else {
finalTransform = finalTransform
.rotated(by: CGFloat(-180.0.radians))
.translatedBy(x: -originalSize.width, y: -originalSize.height)
}
}
layerinstruction.setTransform(finalTransform, at: .zero)
instruction.layerInstructions = [layerinstruction]
videoComposition.instructions = [instruction]
let exporter = AVAssetExportSession(asset: self, presetName: AVAssetExportPresetHighestQuality)
exporter?.videoComposition = videoComposition
exporter?.outputURL = outputURL
exporter?.outputFileType = AVFileType.mov
exporter?.exportAsynchronously(completionHandler: { [weak exporter] in
DispatchQueue.main.async {
if let error = exporter?.error {
completion(.failure(error))
} else {
completion(.success(()))
}
}
})
}
}
Above code is working perfectly fine for cropping of portrait videos but if the video is in the ratio 16:9 then it is not working as expected.