I am picking a video from my swift iOS app using the UIImagePickerController. I am saving this URL and now would like to convert it to data to send to my server for storage using:
let messageVideoData = NSData(contentsOfURL: chosenVideoURL)
The problem is that the file size is very large. For a 7 second video shot on my iPhone 6s the resolution is 1280, 720, the frame rate is 30 and the file size is over 4 MB. I have noticed the same image sent with whatsapp and other chat apps is reduced to a few hundred KB.
What is the best method to reduce the file size for external storage? The video is primarily targeted for phones so reducing the resolution to 800 or less is fine.
I tried setting the UIImagePickerController quality to:
picker.videoQuality = UIImagePickerControllerQualityType.Type640x480
but this only reduced the file size to 3.5 MB.
Using:
picker.videoQuality = UIImagePickerControllerQualityType.TypeLow
reduced the resolution to a value far lower than is desirable.
Is there another approach I should be taking to reduce my video file size for storing on my server?
Try this answer for compress video. According to jojaba's answer:
If you are wanting to compress the video for remote sharing and to keep the original quality for local storage on the iPhone, you should look into AVAssetExportSession or AVAssetWriter.
Compress Video Without Low Quality
This approach is as per Objective-C though.
You should also consider reading on how iOS manages Assets.
//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];
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With