我正在使用UIImagePickerController从我的swift iOS应用中选择一个视频。我正在保存此URL,现在想将其转换为数据,以便使用以下方式发送到我的服务器进行存储:
let messageVideoData = NSData(contentsOfURL: chosenVideoURL)
问题是文件大小非常大。对于iPhone 6s上的7秒视频拍摄,分辨率为1280,720,帧速率为30,文件大小超过4 MB。我注意到与whatsapp和其他聊天应用程序发送的相同图像减少到几百KB。
减少外部存储文件大小的最佳方法是什么?该视频主要针对手机,因此将分辨率降低到800或更低就可以了。
我尝试将UIImagePickerController质量设置为:
picker.videoQuality = UIImagePickerControllerQualityType.Type640x480
但这只会将文件大小减小到3.5 MB。
使用:
picker.videoQuality = UIImagePickerControllerQualityType.TypeLow
将分辨率降低到远低于理想值的值。
我是否应采取另一种方法来减少用于存储在服务器上的视频文件大小?
答案 0 :(得分:2)
尝试压缩视频的答案。根据{{3}}:
如果您想要压缩视频以进行远程共享和 保持iPhone本地存储的原始质量,你应该 查看AVAssetExportSession或AVAssetWriter。
这种方法虽然符合Objective-C。
您还应该考虑阅读iOS如何管理Compress Video Without Low Quality。
答案 1 :(得分:1)
//use SDAVAssetExportSession library with sprcifica bitrate as per requirement
// video file size ~10-15 MB apporox
func aVodzLatestVideoCompressor(inputURL: URL, aOutputURL: URL, aStartTime:Float, aEndTime:Float) {
let startTime = CMTime(seconds: Double(aStartTime), preferredTimescale: 1000)
let endTime = CMTime(seconds: Double(aEndTime), preferredTimescale: 1000)
let timeRange = CMTimeRange(start: startTime, end: endTime)
let anAsset = AVURLAsset(url: inputURL, options: nil)
guard let videoTrack = anAsset.tracks(withMediaType: AVMediaType.video).first else { return }
var aQuality:Float = 0.0
var duration = anAsset.duration
let totalSeconds = Int(CMTimeGetSeconds(duration))
print("duration -\(duration) - totalSeconds -\(totalSeconds)")
var bitrate = min(aQuality, videoTrack.estimatedDataRate)
let landscap = self.isLandScapVideo(afileURL: inputURL )
var originalWidth = videoTrack.naturalSize.width
var originalHeight = videoTrack.naturalSize.height
print("originalWidth -\(originalWidth) originalHeight- \(originalHeight) ")
while (originalWidth >= 1920 || originalHeight >= 1920) {
originalWidth = originalWidth / 2
originalHeight = originalHeight / 2
}
var setWidth = Int(originalWidth)
var setlHeight = Int(originalHeight)
if sizeVideo < 10.0 {
// COMPRESS_QUALITY_HIGH:
setWidth = Int(originalWidth)
setlHeight = Int(originalHeight)
aQuality = Float(setWidth * setlHeight * 20)
bitrate = min(aQuality, videoTrack.estimatedDataRate)
}else if sizeVideo < 20.0 {
//COMPRESS_QUALITY_MEDIUM:
if totalSeconds > 35{
setWidth = Int(originalWidth / 2.7)
setlHeight = Int(originalHeight / 2.7)
}else if totalSeconds > 25 {
setWidth = Int(originalWidth / 2.3)
setlHeight = Int(originalHeight / 2.3)
}else{
setWidth = Int(originalWidth / 2.0)
setlHeight = Int(originalHeight / 2.0)
}
aQuality = Float(setWidth * setlHeight * 10)
bitrate = min(aQuality, videoTrack.estimatedDataRate)
}else if sizeVideo < 30.0 {
//COMPRESS_QUALITY_MEDIUM:
if totalSeconds > 35{
setWidth = Int(originalWidth / 3)
setlHeight = Int(originalHeight / 3)
}else if totalSeconds > 20 {
setWidth = Int(originalWidth / 2.5)
setlHeight = Int(originalHeight / 2.5)
}else{
setWidth = Int(originalWidth / 2.0)
setlHeight = Int(originalHeight / 2.0)
}
aQuality = Float(setWidth * setlHeight * 10)
bitrate = min(aQuality, videoTrack.estimatedDataRate)
}else{
if totalSeconds > 35{
setWidth = Int(originalWidth / 3.0)
setlHeight = Int(originalHeight / 3.0)
}else if totalSeconds > 25 {
setWidth = Int(originalWidth / 2.5)
setlHeight = Int(originalHeight / 2.5)
}else{
setWidth = Int(originalWidth / 2.0)
setlHeight = Int(originalHeight / 2.0)
}
aQuality = Float(setWidth * setlHeight * 10)
bitrate = min(aQuality, videoTrack.estimatedDataRate)
}
print("aQuality")
print(Float(aQuality))
print("bitrate")
print(Float(bitrate))
let encoder = SDAVAssetExportSession(asset: anAsset)
encoder?.shouldOptimizeForNetworkUse = true
encoder?.timeRange = timeRange
encoder?.outputFileType = AVFileType.mp4.rawValue
encoder?.outputURL = aOutputURL
//960 X 540 , 1280 * 720 , 1920*1080 // size reduce parameter
encoder?.videoSettings = [
AVVideoCodecKey: AVVideoCodecType.h264,
AVVideoWidthKey: landscap ? NSNumber(value:1280) : NSNumber(value:720) ,
AVVideoHeightKey: landscap ? NSNumber(value:720) : NSNumber(value:1280),
AVVideoCompressionPropertiesKey: [
AVVideoAverageBitRateKey: NSNumber(value: bitrate),
AVVideoProfileLevelKey: AVVideoProfileLevelH264High40
]
]
encoder?.audioSettings = [
AVFormatIDKey: NSNumber(value: kAudioFormatMPEG4AAC),
AVNumberOfChannelsKey: NSNumber(value: 2),
AVSampleRateKey: NSNumber(value: 44100),
AVEncoderBitRateKey: NSNumber(value: 128000)
]
encoder?.exportAsynchronously(completionHandler: {
if encoder?.status == .completed {
print("Video export succeeded")
DispatchQueue.main.async {
appDelegate.hideLoader()
//NotificationCenter.default.post(name: Notification.Name("getMediaEffect"), object: "3")
//self.sendCompletion?(UIImage(), aOutputURL)
let text = "Original video- \(inputURL.verboseFileSizeInMB()) \n and Compressed video \(aOutputURL.verboseFileSizeInMB()) "
let alertController = UIAlertController.init(title: "Compressed!!", message: text , preferredStyle: .alert)
alertController.addAction(UIAlertAction.init(title: "share to server!", style: .default, handler: { (action) in
// Completion block
NotificationCenter.default.post(name: Notification.Name("getMediaEffect"), object: "3")
self.sendCompletion?(UIImage(), aOutputURL)
}))
alertController.addAction(UIAlertAction.init(title: "Save", style: .default, handler: { (action) in
// Completion block
DispatchQueue.main.async {
appDelegate.hideLoader()
if let videoURL = aOutputURL as? URL{
self.shareVideo(aUrl:videoURL )
}
}
}))
alertController.addAction(UIAlertAction.init(title: "cancel!", style: .default, handler: { (action) in
}))
self.present(alertController, animated: true, completion: nil)
}
} else if encoder?.status == .cancelled {
print("Video export cancelled")
DispatchQueue.main.async {
appDelegate.hideLoader()
self.view.makeToast("error_something_went_wrong".localized)
}
} else {
print("Video export failed with error: \(encoder!.error.localizedDescription) ")
DispatchQueue.main.async {
appDelegate.hideLoader()
self.view.makeToast("error_something_went_wrong".localized)
}
}
})
}
func isLandScapVideo(afileURL: URL) -> Bool{
let resolution = self.resolutionForLocalVideo(url: afileURL)
guard let width = resolution?.width, let height = resolution?.height else {
return false
}
if abs(width) > abs(height){
//landscap
return true
}else{
//potrait
return false
}
}
extension URL {
func verboseFileSizeInMB() -> Float{
let p = self.path
let attr = try? FileManager.default.attributesOfItem(atPath: p)
if let attr = attr {
let fileSize = Float(attr[FileAttributeKey.size] as! UInt64) / (1024.0 * 1024.0)
print(String(format: "FILE SIZE: %.2f MB", fileSize))
return fileSize
} else {
return Float.zero
}
}
}
//Below update if any issue in library at SDAVAssetExportSession library changes below at m file:(changes as per your requirement)
—— CGAffineTransform matrix = CGAffineTransformMakeTranslation(transx / xratio, transy / yratio - transform.ty);
——//fix Orientation - 1
UIImageOrientation videoAssetOrientation = UIImageOrientationUp;
BOOL isVideoAssetPortrait = NO;
CGAffineTransform videoTransform = videoTrack.preferredTransform;
if (videoTransform.a == 0 && videoTransform.b == 1.0 && videoTransform.c == -1.0 && videoTransform.d == 0) {
videoAssetOrientation = UIImageOrientationRight;
isVideoAssetPortrait = YES;
}
if (videoTransform.a == 0 && videoTransform.b == -1.0 && videoTransform.c == 1.0 && videoTransform.d == 0) {
videoAssetOrientation = UIImageOrientationLeft;
isVideoAssetPortrait = YES;
}
if (videoTransform.a == 1.0 && videoTransform.b == 0 && videoTransform.c == 0 && videoTransform.d == 1.0) {
videoAssetOrientation = UIImageOrientationUp;
}
if (videoTransform.a == -1.0 && videoTransform.b == 0 && videoTransform.c == 0 && videoTransform.d == -1.0) {
videoAssetOrientation = UIImageOrientationDown;
}
// [passThroughLayer setTransform:transform atTime:kCMTimeZero];
if ((videoAssetOrientation = UIImageOrientationDown) || (videoAssetOrientation = UIImageOrientationLeft)){
[passThroughLayer setTransform:videoTrack.preferredTransform atTime:kCMTimeZero];
}else{
[passThroughLayer setTransform:transform atTime:kCMTimeZero];
}