ToB企服应用市场:ToB评测及商务社交产业平台
标题:
ios swift 自界说压缩压缩视频(清晰,办理了旋转题目)
[打印本页]
作者:
立聪堂德州十三局店
时间:
2024-8-7 20:03
标题:
ios swift 自界说压缩压缩视频(清晰,办理了旋转题目)
公司最近有一个需求,需要压缩视频,体积和清晰度都有要求。在网上查了很多资料,终极使用了assetReader和assetWriter,但是网上找到的资料,直接拷贝到项目中,都或多或少有些题目,或少了一些参数,所以我把自己这些天查到的,并更改好,能顺利压缩的代码写出来,记录下,也方便各人检察。但这些代码只是适用于我自己的项目,如果各人使用有题目的话,可以留言大概自己网上去探求办理方法。
之前压缩视频,我使用的是苹果提供的一个基于AVFoundation框架的类,AVAssetExportSession,用它来进行视频压缩和转码,使用也是比较方便的,只需设置输出之类和格式等参数。如下图所示,设置比较简单:
func saveVideo(from videoURL: URL,
to videoFinalPath: URL,
handler: ((URL?) -> Void)? = nil) {
let asset = AVAsset(url: videoURL)
if (deleteIfExists(path: videoFinalPath)) {
guard let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset960x540) else {
handler?(nil)
return
}
exporter.outputURL = videoFinalPath
exporter.outputFileType = .mp4
exporter.exportAsynchronously { () -> Void in
switch exporter.status {
case .failed:
let err = exporter.error
print("failed import video: \(exporter.error)")
handler?(nil)
case .cancelled:
print("cancelled import video: \(exporter.error)")
handler?(nil)
case .completed:
print("completed import video save")
handler?(videoFinalPath)
default:
handler?(nil)
break
}
}
}
}
复制代码
此中AVAssetExportPreset960x540就是压缩的质量参数。之前我选择的是AVAssetExportPresetMediumQuality,压缩的体积照旧不错的。压缩的视频是我自己拍摄的41秒的视频,70多MB,能压缩到10多MB,但是视频的清晰度就大打扣头。厥后我选择了AVAssetExportPreset640x480的压缩参数,压缩出来为18.3MB,但是视频照旧不太清晰,但是比之前AVAssetExportPresetMediumQuality的要清晰一点。选 AVAssetExportPreset960x540,压缩出来为27MB,视频这时是比较清晰的,但是体积太大了。参考微信的压缩视频功能,微信中发送视频,默认的压缩出来是13MB多,而且很清晰。所以,上面的参数都达不到这个结果,需要重新找方法去办理。
之后,我使用了assetReader和assetWriter,这个是可以自界说压缩参数并实现更精细的控制,但使用起来相对复杂一些。在使用的时候需要设置好音视频的参数,如果设置不对,大概压缩失败,直接报错闪退。我反复设置了参数后,终于可以压缩,视频的体积和清晰度都能到达想要的结果,但是这时我发现,压缩出来的视频永远是相当于home键在右侧的方向。比如,我拿手机拍一个视频,手机是竖着的,home键朝下,那么压缩出来的视频,便是会逆时针转90度。查了参数,才知道可以通过AVAssetTrack中的preferredTransform检察视频的方向。默认0度就是手机横着,Home键在右边拍摄的视频。如果手机是竖着,Home键在下方,拍摄的视频,那么他的角度是90度。代码如下:
//获取视频的角度
private func degressFromVideoFileWithURL(videoTrack: AVAssetTrack)->Int {
var degress = 0
let t: CGAffineTransform = videoTrack.preferredTransform
if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0){
// Portrait
degress = 90
}else if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0){
// PortraitUpsideDown
degress = 270
}else if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0){
// LandscapeRight
degress = 0
}else if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0){
// LandscapeLeft
degress = 180
}
return degress
}
复制代码
然后,我也找到相识决方法。如果视频的角度不为0,我们在压缩时,直接把视频手动旋转到之前的角度就行了。各人切记,不要再用AVAssetExportSession去旋转方向,网上很多如许的文章,我也试了,转向是成功了,但是之前压缩好的体积,经过AVAssetExportSession再一压缩又变大了……
现在把已经测试过能顺利运行的代码贴出来,供各人参考:
func exportVideo(from videoURL: URL,
to videoFinalPath: URL,
completeHandler: @escaping (URL) -> ()) {
// 创建音视频输入asset
let asset = AVAsset(url: videoURL)
// 原视频大小,用于测试压缩效果
let oldfileItem = try? FileManager.default.attributesOfItem(atPath: videoURL.path)
print(oldfileItem?[FileAttributeKey.size]) //打印之前视频体积
// 创建音视频Reader和Writer
guard let reader = try? AVAssetReader(asset: asset),
let writer = try? AVAssetWriter.init(outputURL: videoFinalPath, fileType: AVFileType.mp4) else { return}
//视频输出配置
let configVideoOutput: [String : Any] = [kCVPixelBufferPixelFormatTypeKey: NSNumber(value: kCVPixelFormatType_422YpCbCr8)] as! [String: Any]
//音频输出配置
let configAudioOutput: [String : Any] = [AVFormatIDKey: NSNumber(value: kAudioFormatLinearPCM)] as! [String: Any]
let compressionProperties: [String: Any] = [AVVideoAverageBitRateKey: 1600 * 1024, //码率
AVVideoExpectedSourceFrameRateKey: 25, //帧率
AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel]
let videoCodeec: String = AVVideoCodecType.h264.rawValue //视频编码
var videoSettings: [String: Any] = [AVVideoCodecKey: videoCodeec, //视频编码
AVVideoWidthKey: 960, //视频宽(必须填写正确,否则压缩后有问题)
AVVideoHeightKey: 540, //视频高(必须填写正确,否则压缩后有问题)
AVVideoCompressionPropertiesKey: compressionProperties,
AVVideoScalingModeKey: AVVideoScalingModeResizeAspectFill] //设置视频缩放方式
let stereoChannelLayout: AudioChannelLayout = AudioChannelLayout(mChannelLayoutTag: kAudioChannelLayoutTag_Stereo,
mChannelBitmap: AudioChannelBitmap.init(rawValue: 0),
mNumberChannelDescriptions: 0,
mChannelDescriptions: AudioChannelDescription())
let channelLayoutAsData: NSData = NSData(bytes: (stereoChannelLayout as? UnsafeRawPointer), length: MemoryLayout.size(ofValue: AudioChannelLayout.self))
let audioSettings: [String: Any] = [AVFormatIDKey : kAudioFormatMPEG4AAC,
AVEncoderBitRateKey : 96000, // 码率
AVSampleRateKey : 44100, // 采样率
AVChannelLayoutKey : channelLayoutAsData,
AVNumberOfChannelsKey : 2]
// video part
guard let videoTrack: AVAssetTrack = (asset.tracks(withMediaType: .video)).first else {return}
//获取原视频的角度
let degree = self.degressFromVideoFileWithURL(videoTrack: videoTrack)
//获取原视频的宽高,如果是手机拍摄,一般是宽大,高小,如果是手机自带录屏,那么是高大,宽小
let naturalSize = videoTrack.naturalSize
if naturalSize.width < naturalSize.height {
videoSettings[AVVideoWidthKey] = 540
videoSettings[AVVideoHeightKey] = 960
}
let videoOutput: AVAssetReaderTrackOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: configVideoOutput)
let videoInput: AVAssetWriterInput = AVAssetWriterInput(mediaType: .video, outputSettings: videoSettings)
//视频写入的旋转(这句很重要)
if let transform = self.getAffineTransform(degree: degree, videoTrack: videoTrack) {
videoInput.transform = transform
}
if reader.canAdd(videoOutput) {
reader.add(videoOutput)
}
if writer.canAdd(videoInput) {
writer.add(videoInput)
}
// audio part
guard let audioTrack: AVAssetTrack = (asset.tracks(withMediaType: .audio)).first else {return}
let audioOutput: AVAssetReaderTrackOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: configAudioOutput)
let audioInput: AVAssetWriterInput = AVAssetWriterInput(mediaType: .audio, outputSettings: audioSettings)
if reader.canAdd(audioOutput) {
reader.add(audioOutput)
}
if writer.canAdd(audioInput) {
writer.add(audioInput)
}
// 开始读写
reader.startReading()
writer.startWriting()
writer.startSession(atSourceTime: .zero)
let group = DispatchGroup()
group.enter()
videoInput.requestMediaDataWhenReady(on: DispatchQueue(label: "videoOutQueue"), using: {
var completedOrFailed = false
while (videoInput.isReadyForMoreMediaData) && !completedOrFailed {
let sampleBuffer: CMSampleBuffer? = videoOutput.copyNextSampleBuffer()
if sampleBuffer != nil {//}&& reader.status == .reading {
let result = videoInput.append(sampleBuffer!)
// if (!result) {
// reader.cancelReading()
// break
// }
} else {
completedOrFailed = true
videoInput.markAsFinished()
group.leave()
break
}
}
})
group.enter()
audioInput.requestMediaDataWhenReady(on: DispatchQueue(label: "audioOutQueue"), using: {
var completedOrFailed = false
while (audioInput.isReadyForMoreMediaData) && !completedOrFailed {
let sampleBuffer: CMSampleBuffer? = audioOutput.copyNextSampleBuffer()
if sampleBuffer != nil {//}&& reader.status == .reading {
let result = audioInput.append(sampleBuffer!)
// if (!result) {
// reader.cancelReading()
// break
// }
} else {
completedOrFailed = true
audioInput.markAsFinished()
group.leave()
break
}
}
})
group.notify(queue: DispatchQueue.main) {
if reader.status == .reading {
reader.cancelReading()
}
switch writer.status {
case .writing:
writer.finishWriting(completionHandler: {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3, execute: {
let newfileSize = try? FileManager.default.attributesOfItem(atPath: videoFinalPath.path)
print(newfileSize?[FileAttributeKey.size]) //打印压缩后视频的体积
completeHandler(videoFinalPath)
})
})
case .cancelled:
print("$$$ compress cancelled")
case .failed:
print("$$$ compress failed",writer.error)
case .completed:
print("$$$ compress completed")
case .unknown:
print("$$$ compress unknown")
}
}
}
复制代码
private func getAffineTransform(degree: Int, videoTrack: AVAssetTrack)-> CGAffineTransform? {
var translateToCenter: CGAffineTransform?
var mixedTransform: CGAffineTransform?
if degree == 90 { //视频旋转90度,home按键在左"
translateToCenter = CGAffineTransform(translationX: videoTrack.naturalSize.height, y: 0.0)
mixedTransform = translateToCenter!.rotated(by: Double.pi / 2)
} else if degree == 180 { //视频旋转180度,home按键在上"
translateToCenter = CGAffineTransform(translationX: videoTrack.naturalSize.width, y: videoTrack.naturalSize.height)
mixedTransform = translateToCenter!.rotated(by: Double.pi)
} else if degree == 270 { //视频旋转270度,home按键在右"
translateToCenter = CGAffineTransform(translationX: 0.0, y: videoTrack.naturalSize.width)
mixedTransform = translateToCenter!.rotated(by: Double.pi / 2 * 3)
}
return mixedTransform
}
复制代码
以上便是所有代码,渴望对各人有帮助!另外关于压缩视频的参数,有篇文章写得非常详细,链接为:手把手教你iOS自界说视频压缩 - 知乎
各人可以在视频压缩出来后,参照文章的参数,进行调整,以便到达最佳结果。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4