ios - 视频大小调整不符合请求的大小
ios - video resizing not respecting requested size
我正在尝试在我的 ios 应用中调整视频的大小。我的代码如下:
fileprivate func resize(url: URL, height: CGFloat, completion: @escaping ((URL) -> Void)) {
let video = AVAsset(url: url)
guard let track = video.tracks(withMediaType: AVMediaTypeVideo).first
else { return }
let size = track.naturalSize
// detect video rotation
let txf = track.preferredTransform
let videoAngle = atan2(txf.b, txf.a)
let isRotated = videoAngle == CGFloat(M_PI_2) || videoAngle == CGFloat(M_PI_2 * 3)
let videoW = isRotated ? size.height : size.width
let videoH = isRotated ? size.width : size.height
// get output width that keeps aspect ratio
let width = height / videoH * videoW
print("desired width \(width) height \(height)")
print("original width \(size.width) height \(size.height)")
// resize the video
let videoComposition = AVMutableVideoComposition()
videoComposition.renderSize = CGSize(width: width, height: height)
videoComposition.frameDuration = CMTimeMake(1, 30)
let instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, video.duration)
videoComposition.instructions = [instruction]
let outputURL = Constants.cacheDirectory.appendingPathComponent(
"\(String.random(ofLength: 10)).mp4")
guard let exporter = AVAssetExportSession(
asset: video,
presetName: AVAssetExportPresetMediumQuality
) else { return }
exporter.videoComposition = videoComposition
exporter.outputURL = outputURL
exporter.outputFileType = AVFileTypeQuickTimeMovie
exporter.exportAsynchronously(completionHandler: { () -> Void in
DispatchQueue.main.async {
let asset = AVAsset(url: outputURL)
let track = asset.tracks(withMediaType: AVMediaTypeVideo).first!
print("result size \(track.naturalSize)")
completion(outputURL)
}
})
}
当我在 1280.0 x 720.0
且旋转 90 度的视频上将 resize
调用到 720
的高度时,我得到的结果是 320.0 x 568.0
并且我的日志是:
desired width 405.0 height 720.0
original width 1280.0 height 720.0
result size (320.0, 568.0)
我什至找不到这些数字来自何处的关系。唯一的问题是宽高比保持不变。
我最终采取了另一种方式,因为视频调整大小似乎比 ios 上应该复杂得多。 SDAVAssetExportSession makes a great job at handling all that, and if you look at the code还有很多事要做。
无论如何,我的最终代码如下:
import SDAVAssetExportSession
fileprivate func resize(url: URL, height: CGFloat, completion: @escaping ((Error?) -> Void)) {
let video = AVAsset(url: url)
guard let track = video.tracks(withMediaType: AVMediaTypeVideo).first
else { return }
let size = track.naturalSize
let txf = track.preferredTransform
let videoAngle = atan2(txf.b, txf.a)
let isRotated = videoAngle == CGFloat(M_PI_2) || videoAngle == CGFloat(M_PI_2 * 3)
let videoW = isRotated ? size.height : size.width
let videoH = isRotated ? size.width : size.height
let width = height / videoH * videoW
let outputURL = Constants.cacheDirectory.appendingPathComponent(
"\(String.random(ofLength: 10)).mp4")
if let encoder = SDAVAssetExportSession(asset: AVAsset(url: url)) {
encoder.outputFileType = AVFileTypeMPEG4
encoder.outputURL = outputURL
encoder.videoSettings = [
AVVideoCodecKey: AVVideoCodecH264,
AVVideoWidthKey: width,
AVVideoHeightKey: height,
AVVideoCompressionPropertiesKey: [
AVVideoAverageBitRateKey: 1000000,
AVVideoProfileLevelKey: AVVideoProfileLevelH264High40,
AVVideoMaxKeyFrameIntervalKey: 60,
],
]
encoder.exportAsynchronously(completionHandler: {
if encoder.status == AVAssetExportSessionStatus.completed {
DispatchQueue.main.async {
self.url = outputURL
let asset = AVAsset(url: outputURL)
let track = asset.tracks(withMediaType: AVMediaTypeVideo).first!
completion(nil)
}
} else if encoder.status == AVAssetExportSessionStatus.cancelled {
completion(nil)
} else {
completion(encoder.error)
}
})
}
}
我正在尝试在我的 ios 应用中调整视频的大小。我的代码如下:
fileprivate func resize(url: URL, height: CGFloat, completion: @escaping ((URL) -> Void)) {
let video = AVAsset(url: url)
guard let track = video.tracks(withMediaType: AVMediaTypeVideo).first
else { return }
let size = track.naturalSize
// detect video rotation
let txf = track.preferredTransform
let videoAngle = atan2(txf.b, txf.a)
let isRotated = videoAngle == CGFloat(M_PI_2) || videoAngle == CGFloat(M_PI_2 * 3)
let videoW = isRotated ? size.height : size.width
let videoH = isRotated ? size.width : size.height
// get output width that keeps aspect ratio
let width = height / videoH * videoW
print("desired width \(width) height \(height)")
print("original width \(size.width) height \(size.height)")
// resize the video
let videoComposition = AVMutableVideoComposition()
videoComposition.renderSize = CGSize(width: width, height: height)
videoComposition.frameDuration = CMTimeMake(1, 30)
let instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, video.duration)
videoComposition.instructions = [instruction]
let outputURL = Constants.cacheDirectory.appendingPathComponent(
"\(String.random(ofLength: 10)).mp4")
guard let exporter = AVAssetExportSession(
asset: video,
presetName: AVAssetExportPresetMediumQuality
) else { return }
exporter.videoComposition = videoComposition
exporter.outputURL = outputURL
exporter.outputFileType = AVFileTypeQuickTimeMovie
exporter.exportAsynchronously(completionHandler: { () -> Void in
DispatchQueue.main.async {
let asset = AVAsset(url: outputURL)
let track = asset.tracks(withMediaType: AVMediaTypeVideo).first!
print("result size \(track.naturalSize)")
completion(outputURL)
}
})
}
当我在 1280.0 x 720.0
且旋转 90 度的视频上将 resize
调用到 720
的高度时,我得到的结果是 320.0 x 568.0
并且我的日志是:
desired width 405.0 height 720.0
original width 1280.0 height 720.0
result size (320.0, 568.0)
我什至找不到这些数字来自何处的关系。唯一的问题是宽高比保持不变。
我最终采取了另一种方式,因为视频调整大小似乎比 ios 上应该复杂得多。 SDAVAssetExportSession makes a great job at handling all that, and if you look at the code还有很多事要做。
无论如何,我的最终代码如下:
import SDAVAssetExportSession
fileprivate func resize(url: URL, height: CGFloat, completion: @escaping ((Error?) -> Void)) {
let video = AVAsset(url: url)
guard let track = video.tracks(withMediaType: AVMediaTypeVideo).first
else { return }
let size = track.naturalSize
let txf = track.preferredTransform
let videoAngle = atan2(txf.b, txf.a)
let isRotated = videoAngle == CGFloat(M_PI_2) || videoAngle == CGFloat(M_PI_2 * 3)
let videoW = isRotated ? size.height : size.width
let videoH = isRotated ? size.width : size.height
let width = height / videoH * videoW
let outputURL = Constants.cacheDirectory.appendingPathComponent(
"\(String.random(ofLength: 10)).mp4")
if let encoder = SDAVAssetExportSession(asset: AVAsset(url: url)) {
encoder.outputFileType = AVFileTypeMPEG4
encoder.outputURL = outputURL
encoder.videoSettings = [
AVVideoCodecKey: AVVideoCodecH264,
AVVideoWidthKey: width,
AVVideoHeightKey: height,
AVVideoCompressionPropertiesKey: [
AVVideoAverageBitRateKey: 1000000,
AVVideoProfileLevelKey: AVVideoProfileLevelH264High40,
AVVideoMaxKeyFrameIntervalKey: 60,
],
]
encoder.exportAsynchronously(completionHandler: {
if encoder.status == AVAssetExportSessionStatus.completed {
DispatchQueue.main.async {
self.url = outputURL
let asset = AVAsset(url: outputURL)
let track = asset.tracks(withMediaType: AVMediaTypeVideo).first!
completion(nil)
}
} else if encoder.status == AVAssetExportSessionStatus.cancelled {
completion(nil)
} else {
completion(encoder.error)
}
})
}
}