在 iPhone 上重新编码从用户照片库中选择的任意视频文件以匹配所需参数的最简单方法是什么?即:720p(方面填充)、人像(无变换)、30fps、h264。
示例:给定具有以下参数的视频:1080x1920,90d 旋转(即使它的宽度小于高度,它也应该被旋转并横向播放),24fps,hvec。它将变为:720p(在这种情况下,纵横比是相同的。如果不同,它将具有裁剪/缩放的纵横比填充)、人像(没有变换)、30fps、h264。
这是我到目前为止所尝试的:
// Make an asset object from the input video url
let asset = AVURLAsset(url: videoUrl)
// We will be using the AVAssetWriter class to output the video and it requires an AVAssetReader as input
var reader: AVAssetReader? = nil
do {
reader = try AVAssetReader(asset: asset)
} catch {
error.record(with: "Unable to create AVAssetReader from asset")
completion(.failure(error))
return
}
// Get the video track and make sure we have a valid reader
guard
let assetReader = reader,
let videoTrack = asset.tracks(withMediaType: AVMediaType.video).first
else {
completion(.failure(VideoHelperError.unknown))
return
}
// Set the video properties that we want
let videoReaderSettings: [String:Any] = [kCVPixelBufferPixelFormatTypeKey as String:kCVPixelFormatType_32ARGB ]
let videoSettings: [String:Any] = [
AVVideoScalingModeKey: AVVideoScalingModeResizeAspectFill,
AVVideoCodecKey: AVVideoCodecType.h264,
AVVideoHeightKey: 1280,
AVVideoWidthKey: 720
]
let assetReaderVideoOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: videoReaderSettings)
if assetReader.canAdd(assetReaderVideoOutput) {
assetReader.add(assetReaderVideoOutput)
} else {
completion(.failure(VideoHelperError.unknown))
return
}
// Start preparing the writer
let videoInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoSettings)
videoInput.transform = videoTrack.preferredTransform
// This is the queue we will be using for feeding the video into the writer
let videoInputQueue = DispatchQueue(label: "videoQueue")
// Create the temporary file onto which we will export our result
guard let outputUrl = try? FileManager.tempFileURL(withFileExtension: "mp4") else {
completion(.failure(VideoHelperError.fileSystemFailure))
return
}
// Create the writer that will generate the output file
var writer: AVAssetWriter? = nil
do {
writer = try AVAssetWriter(outputURL: outputUrl, fileType: AVFileType.mp4)
} catch {
error.record(with: "Unable to create AVAssetWriter")
completion(.failure(error))
return
}
guard let assetWriter = writer else{
completion(.failure(VideoHelperError.unknown))
return
}
// Add the writer input to the writer and start read/write of the video file
assetWriter.shouldOptimizeForNetworkUse = true
assetWriter.add(videoInput)
assetWriter.startWriting()
assetReader.startReading()
assetWriter.startSession(atSourceTime: CMTime.zero)
// As data becomes available, we should pass it through the writer
videoInput.requestMediaDataWhenReady(on: videoInputQueue) {
while(videoInput.isReadyForMoreMediaData){
let sample = assetReaderVideoOutput.copyNextSampleBuffer()
if (sample != nil) {
videoInput.append(sample!)
} else {
videoInput.markAsFinished()
DispatchQueue.main.async {
assetWriter.finishWriting(completionHandler: {
completion(.success(assetWriter.outputURL))
})
assetReader.cancelReading()
}
break;
}
}
}
上面的代码实现了 4 个目标中的 2 个。它缩放/裁剪到 720p(纵横比填充)并以正确的格式进行编码。但是,它不会针对 FPS 进行调整或正确移除旋转,从而导致某些视频最终超出帧并显示为空白。
如果 fps 更高,它将被抽取和调整(60fps -> 30fps)。如果较低,它将以编程方式生成新的填充帧(24fps -> 30fps)。不改变长度(以自然速度播放)。有没有内置的方法来做到这一点?
在 aspectfill 拟合之前应用视频转换以确保内容始终位于导出资产的视口内的最佳方法是什么?
参考