裁剪视频Size
使用 AVFoundation如何裁剪视频大小,比如 原视频为 width: 920, height: 1680 我想裁剪为 width: 920, height: 1680 * 0.6 但是是从 1680 * 0.1开始裁剪
使用 AVFoundation 裁剪视频大小,可以通过 AVMutableComposition 和 AVVideoComposition 来实现。以下是具体的步骤和代码示例,展示如何从原始视频中裁剪出指定区域(例如从高度 1680 * 0.1 开始,裁剪出高度为 1680 * 0.6 的区域)。
实现步骤
-
加载视频资源:
- 使用
AVAsset加载原始视频。
- 使用
-
创建
AVMutableComposition:- 用于将视频和音频轨道添加到新的组合中。
-
设置裁剪区域:
- 使用
AVVideoComposition的renderSize和instructions来定义裁剪区域。
- 使用
-
导出视频:
- 使用
AVAssetExportSession将裁剪后的视频导出到文件。
- 使用
完整代码实现
import UIKit
import AVFoundation
import Photos
class VideoCroppingViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 1. 加载本地视频
guard let videoURL = Bundle.main.url(forResource: "input_video", withExtension: "mp4") else {
print("视频文件未找到")
return
}
// 2. 定义裁剪区域
let originalWidth: CGFloat = 920
let originalHeight: CGFloat = 1680
let cropStartY: CGFloat = originalHeight * 0.1 // 从高度的 10% 开始裁剪
let cropHeight: CGFloat = originalHeight * 0.6 // 裁剪高度为 60%
let cropRect = CGRect(x: 0, y: cropStartY, width: originalWidth, height: cropHeight)
// 3. 裁剪视频
cropVideo(sourceURL: videoURL, cropRect: cropRect) { outputURL in
if let outputURL = outputURL {
print("裁剪后的视频已保存到: \(outputURL)")
// 4. 保存到相册
self.saveVideoToPhotoLibrary(url: outputURL)
} else {
print("视频裁剪失败")
}
}
}
// 裁剪视频
func cropVideo(sourceURL: URL, cropRect: CGRect, completion: @escaping (URL?) -> Void) {
// 1. 加载视频资源
let asset = AVURLAsset(url: sourceURL)
// 2. 创建 AVMutableComposition
let composition = AVMutableComposition()
// 3. 添加视频轨道
guard let videoTrack = asset.tracks(withMediaType: .video).first else {
print("无法获取视频轨道")
completion(nil)
return
}
let compositionVideoTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
do {
try compositionVideoTrack?.insertTimeRange(CMTimeRange(start: .zero, duration: asset.duration), of: videoTrack, at: .zero)
} catch {
print("插入视频轨道失败: \(error.localizedDescription)")
completion(nil)
return
}
// 4. 添加音频轨道
if let audioTrack = asset.tracks(withMediaType: .audio).first {
let compositionAudioTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
do {
try compositionAudioTrack?.insertTimeRange(CMTimeRange(start: .zero, duration: asset.duration), of: audioTrack, at: .zero)
} catch {
print("插入音频轨道失败: \(error.localizedDescription)")
}
}
// 5. 创建 AVVideoComposition
let videoComposition = AVMutableVideoComposition()
videoComposition.renderSize = CGSize(width: cropRect.width, height: cropRect.height)
videoComposition.frameDuration = CMTime(value: 1, timescale: 30) // 30 FPS
// 6. 创建视频指令
let instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = CMTimeRange(start: .zero, duration: asset.duration)
let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: compositionVideoTrack!)
// 设置裁剪区域
let transform = CGAffineTransform(translationX: -cropRect.origin.x, y: -cropRect.origin.y)
layerInstruction.setTransform(transform, at: .zero)
instruction.layerInstructions = [layerInstruction]
videoComposition.instructions = [instruction]
// 7. 导出视频
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
let outputPath = documentsPath.appendingPathComponent("cropped_video.mov")
let outputURL = URL(fileURLWithPath: outputPath)
// 删除已存在的文件(如果存在)
if FileManager.default.fileExists(atPath: outputPath) {
try? FileManager.default.removeItem(at: outputURL)
}
let exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality)
exporter?.outputURL = outputURL
exporter?.outputFileType = .mov
exporter?.videoComposition = videoComposition
exporter?.exportAsynchronously {
DispatchQueue.main.async {
if exporter?.status == .completed {
completion(outputURL)
} else {
print("导出失败: \(exporter?.error?.localizedDescription ?? "未知错误")")
completion(nil)
}
}
}
}
// 保存视频到相册
func saveVideoToPhotoLibrary(url: URL) {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url)
}) { success, error in
if success {
print("视频已保存到相册")
} else if let error = error {
print("保存视频到相册失败: \(error.localizedDescription)")
}
}
}
}
代码说明
-
裁剪区域:
cropRect定义了裁剪的区域,x和y是裁剪的起始点,width和height是裁剪的尺寸。
-
视频轨道和音频轨道:
- 使用
AVMutableComposition将原始视频的轨道插入到新的组合中。
- 使用
-
视频指令:
- 使用
AVMutableVideoCompositionInstruction和AVMutableVideoCompositionLayerInstruction设置裁剪区域的变换。
- 使用
-
导出视频:
- 使用
AVAssetExportSession将裁剪后的视频导出到文件。
- 使用
-
保存到相册:
- 使用
PHPhotoLibrary将裁剪后的视频保存到相册。
- 使用
运行结果
- 视频将从指定区域裁剪。
- 裁剪后的视频会保存到相册。
- 控制台会输出裁剪后的视频文件路径。
注意事项
- 确保原始视频的分辨率和裁剪区域是合理的,否则可能会导致黑边或变形。
- 如果视频较大,导出操作可能需要一些时间。
- 在
Info.plist中添加相册访问权限:<key>NSPhotoLibraryAddUsageDescription</key> <string>我们需要保存视频到相册</string>
通过这种方式,你可以使用 AVFoundation 实现视频的精确裁剪。
裁剪时长
在iOS中,使用Swift来拍摄视频并裁剪视频大小,涉及到两个主要的步骤:
- 拍摄视频:通过
UIImagePickerController或者AVCaptureSession来拍摄视频。 - 裁剪视频大小:利用
AVAsset和AVAssetExportSession来裁剪视频的大小。
下面是实现这两个步骤的代码示例:
1. 使用UIImagePickerController拍摄视频
首先,你需要请求相机权限,并使用UIImagePickerController来拍摄视频。
import UIKit
import AVFoundation
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// 检查是否有摄像头权限
checkCameraPermission()
}
func checkCameraPermission() {
AVCaptureDevice.requestAccess(for: .video) { response in
if response {
// 可以访问摄像头
self.recordVideo()
} else {
// 没有权限
print("Camera access denied")
}
}
}
func recordVideo() {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = .camera
imagePicker.mediaTypes = ["public.movie"]
imagePicker.delegate = self
present(imagePicker, animated: true, completion: nil)
}
// 处理视频选取完成后的回调
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as? URL {
print("Video URL: \(videoURL)")
// 处理裁剪视频
self.trimVideo(videoURL: videoURL)
}
picker.dismiss(animated: true, completion: nil)
}
// 处理视频裁剪
func trimVideo(videoURL: URL) {
let asset = AVAsset(url: videoURL)
let startTime = CMTime(seconds: 5, preferredTimescale: 600) // 从第5秒开始
let duration = CMTime(seconds: 10, preferredTimescale: 600) // 视频裁剪的持续时间为10秒
let trimmedAsset = AVAsset(url: videoURL)
let trimmedVideoURL = getDocumentsDirectory().appendingPathComponent("trimmedVideo.mp4")
let exportSession = AVAssetExportSession(asset: trimmedAsset, presetName: AVAssetExportPresetHighestQuality)
exportSession?.outputURL = trimmedVideoURL
exportSession?.outputFileType = .mp4
exportSession?.timeRange = CMTimeRangeMake(start: startTime, duration: duration)
exportSession?.exportAsynchronously {
switch exportSession?.status {
case .completed:
print("Video trimming successful!")
print("Trimmed video URL: \(trimmedVideoURL)")
case .failed:
print("Video trimming failed: \(exportSession?.error?.localizedDescription ?? "")")
default:
break
}
}
}
// 获取文件保存路径
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
}
说明:
- 录制视频:我们通过
UIImagePickerController来拍摄视频,选取的媒体类型为public.movie,允许拍摄视频。 - 裁剪视频:使用
AVAsset来加载选中的视频,通过AVAssetExportSession裁剪视频。这里裁剪的是从视频的第5秒到第15秒(裁剪10秒的视频)。输出为.mp4格式。 - 保存视频:裁剪后的视频保存到文档目录。
注意事项:
- 确保在
Info.plist中配置了摄像头和麦克风权限:NSCameraUsageDescription:描述为何需要使用相机。NSMicrophoneUsageDescription:描述为何需要使用麦克风。
- 根据需要调整裁剪的视频起始时间和持续时间。
这种方法适用于在简单的视频录制和裁剪需求场景下。